diff --git a/src/main/java/world/bentobox/bentobox/api/commands/BentoBoxCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/BentoBoxCommand.java index 5d2e91817..e8b2dc9b4 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/BentoBoxCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/BentoBoxCommand.java @@ -23,12 +23,27 @@ public interface BentoBoxCommand { */ void setup(); + /** + * Returns whether the command can be executed by this user or not. + * It is recommended to send messages to let this user know why they could not execute the command. + * Note that this is run previous to {@link #execute(User, String, List)}. + * @param user the {@link User} who is executing this command. + * @param label the label which has been used to execute this command. + * It can be {@link CompositeCommand#getLabel()} or an alias. + * @param args the command arguments. + * @return {@code true} if this command can be executed, {@code false} otherwise. + * @since 1.3.0 + */ + default boolean canExecute(User user, String label, List args) { + return true; + } + /** * Defines what will be executed when this command is run. - * @param user The {@link User} who is executing this command. - * @param label The label which has been used to execute this command. - * It can be {@link CompositeCommand#getLabel()} or an alias. - * @param args The command arguments. + * @param user the {@link User} who is executing this command. + * @param label the label which has been used to execute this command. + * It can be {@link CompositeCommand#getLabel()} or an alias. + * @param args the command arguments. * @return {@code true} if the command executed successfully, {@code false} otherwise. */ boolean execute(User user, String label, List args); @@ -37,9 +52,9 @@ public interface BentoBoxCommand { * Tab Completer for CompositeCommands. * Note that any registered sub-commands will be automatically added to the list. * Use this to add tab-complete for things like names. - * @param user The {@link User} who is executing this command. - * @param alias Alias for command - * @param args Command arguments + * @param user the {@link User} who is executing this command. + * @param alias alias for command + * @param args command arguments * @return List of strings that could be used to complete this command. */ default Optional> tabComplete(User user, String alias, List args) { 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 88bc79c2b..8c42065aa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -244,7 +244,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi // Set the user's addon context user.setAddon(addon); // Execute and trim args - return cmd.execute(user, (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label, Arrays.asList(args).subList(cmd.subCommandLevel, args.length)); + + String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label; + List cmdArgs = Arrays.asList(args).subList(cmd.subCommandLevel, args.length); + + return cmd.canExecute(user, cmdLabel, cmdArgs) && cmd.execute(user, cmdLabel, cmdArgs); } /** diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java index 10ae4cd2e..05037fe22 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java @@ -26,12 +26,7 @@ public class AdminDeleteCommand extends ConfirmableCommand { } @Override - public boolean execute(User user, String label, List args) { - // If args are not right, show help - if (args.size() != 1) { - showHelp(this, user); - return false; - } + public boolean canExecute(User user, String label, List args) { // Get target UUID targetUUID = getPlayers().getUUID(args.get(0)); if (targetUUID == null) { @@ -47,6 +42,18 @@ public class AdminDeleteCommand extends ConfirmableCommand { user.sendMessage("commands.admin.delete.cannot-delete-owner"); return false; } + return true; + } + + @Override + public boolean execute(User user, String label, List args) { + // If args are not right, show help + if (args.size() != 1) { + showHelp(this, user); + return false; + } + // Get target + UUID targetUUID = getPlayers().getUUID(args.get(0)); // Confirm askConfirmation(user, () -> deletePlayer(user, targetUUID)); return true; diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java index 8feb758f1..3260a4be1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSettingsCommand.java @@ -25,14 +25,19 @@ public class IslandSettingsCommand extends CompositeCommand { } @Override - public boolean execute(User user, String label, List args) { + public boolean canExecute(User user, String label, List args) { // Settings are only shown if you are in the right world if (Util.getWorld(user.getWorld()).equals(getWorld())) { - SettingsPanel.openPanel(getPlugin(), user, Flag.Type.PROTECTION, getWorld(), 0); return true; } else { user.sendMessage("general.errors.wrong-world"); return false; } } + + @Override + public boolean execute(User user, String label, List args) { + SettingsPanel.openPanel(getPlugin(), user, Flag.Type.PROTECTION, getWorld(), 0); + return true; + } } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java index 54f854932..743702856 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java @@ -32,5 +32,4 @@ public class BentoBoxAboutCommand extends CompositeCommand { 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 00fcaf27f..1cc87a4e0 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java @@ -27,5 +27,4 @@ public class BentoBoxCommand extends CompositeCommand { showHelp(this, user); return true; } - } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxVersionCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxVersionCommand.java index 35dd73774..bbd9c9ffb 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxVersionCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxVersionCommand.java @@ -45,5 +45,4 @@ public class BentoBoxVersionCommand extends CompositeCommand { return true; } - } 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 fd1341d09..3cd69a617 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 @@ -3,7 +3,9 @@ package world.bentobox.bentobox.listeners.flags.protection; import org.bukkit.Material; import org.bukkit.entity.Animals; import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Boat; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Minecart; import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -22,24 +24,34 @@ import world.bentobox.bentobox.lists.Flags; public class EntityInteractListener extends FlagListener { @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) - public void onPlayerInteract(final PlayerInteractAtEntityEvent e) { + public void onPlayerInteractAtEntity(final PlayerInteractAtEntityEvent e) { if (e.getRightClicked() instanceof ArmorStand) { checkIsland(e, e.getRightClicked().getLocation(), Flags.ARMOR_STAND); } } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onPlayerHitEntity(PlayerInteractEntityEvent e) { - // Animal riding - if (e.getRightClicked() instanceof Vehicle && e.getRightClicked() instanceof Animals) { - checkIsland(e, e.getRightClicked().getLocation(), Flags.RIDING); + public void onPlayerInteractEntity(PlayerInteractEntityEvent e) { + if (e.getRightClicked() instanceof Vehicle) { + // Animal riding + if (e.getRightClicked() instanceof Animals) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.RIDING); + } + // Minecart riding + else if (e.getRightClicked() instanceof Minecart) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.MINECART); + } + // Boat riding + else if (e.getRightClicked() instanceof Boat) { + checkIsland(e, e.getRightClicked().getLocation(), Flags.BOAT); + } } // Villager trading - if (e.getRightClicked().getType().equals(EntityType.VILLAGER)) { + else if (e.getRightClicked().getType().equals(EntityType.VILLAGER)) { checkIsland(e, e.getRightClicked().getLocation(), Flags.TRADING); } // Name tags - if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG)) { + else if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.NAME_TAG)) { checkIsland(e, e.getRightClicked().getLocation(), Flags.NAME_TAG); } } 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 a135b1061..3e34746ad 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 @@ -65,20 +65,22 @@ public class PlaceBlocksListener extends FlagListener { case ACTIVATOR_RAIL: if (e.getMaterial() != null && (e.getMaterial() == Material.MINECART || e.getMaterial() == Material.CHEST_MINECART || e.getMaterial() == Material.HOPPER_MINECART || e.getMaterial() == Material.TNT_MINECART || e.getMaterial() == Material.FURNACE_MINECART)) { - checkIsland(e, e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS); + checkIsland(e, e.getClickedBlock().getLocation(), Flags.MINECART); } return; default: // Check in-hand items - if (e.getMaterial() != null - && (e.getMaterial().equals(Material.FIREWORK_ROCKET) - || e.getMaterial().equals(Material.ARMOR_STAND) - || e.getMaterial().equals(Material.END_CRYSTAL) - //|| Tag.DOORS.isTagged(e.getMaterial()) - || e.getMaterial().equals(Material.CHEST) || e.getMaterial().equals(Material.TRAPPED_CHEST) - || (e.getMaterial().name().contains("BOAT") - && !e.getClickedBlock().isLiquid()))) { - checkIsland(e, e.getPlayer().getLocation(), Flags.PLACE_BLOCKS); + if (e.getMaterial() != null) { + if (e.getMaterial().equals(Material.FIREWORK_ROCKET) + || e.getMaterial().equals(Material.ARMOR_STAND) + || e.getMaterial().equals(Material.END_CRYSTAL) + //|| Tag.DOORS.isTagged(e.getMaterial()) + || e.getMaterial().equals(Material.CHEST) || e.getMaterial().equals(Material.TRAPPED_CHEST)) { + checkIsland(e, e.getPlayer().getLocation(), Flags.PLACE_BLOCKS); + } + else if (e.getMaterial().name().contains("BOAT")) { + checkIsland(e, e.getPlayer().getLocation(), Flags.BOAT); + } } } } diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 21fa03fe4..606ee62d6 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -107,6 +107,20 @@ public final class Flags { // Entity interactions public static final Flag ARMOR_STAND = new Flag.Builder("ARMOR_STAND", Material.ARMOR_STAND).listener(new EntityInteractListener()).build(); public static final Flag RIDING = new Flag.Builder("RIDING", Material.GOLDEN_HORSE_ARMOR).build(); + /** + * Prevents players from issuing any kind of interactions with Minecarts (entering, placing and opening if chest). + * @since 1.3.0 + * @see EntityInteractListener + * @see PlaceBlocksListener + */ + public static final Flag MINECART = new Flag.Builder("MINECART", Material.MINECART).build(); + /** + * Prevents players from issuing any kind of interactions with Boats (entering, placing). + * @since 1.3.0 + * @see EntityInteractListener + * @see PlaceBlocksListener + */ + public static final Flag BOAT = new Flag.Builder("BOAT", Material.OAK_BOAT).build(); public static final Flag TRADING = new Flag.Builder("TRADING", Material.EMERALD).defaultSetting(true).build(); public static final Flag NAME_TAG = new Flag.Builder("NAME_TAG", Material.NAME_TAG).build(); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 3d7ea15b7..7ea0492f9 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -439,6 +439,10 @@ protection: description: "Toggle interaction" name: "Beds" hint: "Bed use disabled" + BOAT: + name: "Boats" + description: "Toggle boats interactions" + hint: "No boat interaction allowed" BREAK_BLOCKS: description: "Toggle breaking" name: "Break blocks" @@ -674,6 +678,10 @@ protection: description: "Toggle cow milking" name: "Milking" hint: "No milking allowed" + MINECART: + name: "Minecarts" + description: "Toggle minecart interactions" + hint: "No minecart interaction allowed" MONSTER_SPAWN: description: "Toggle spawning" name: "Monster spawning" diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java index 22c9a212f..f54dee04e 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java @@ -140,7 +140,7 @@ public class AdminDeleteCommandTest { AdminDeleteCommand itl = new AdminDeleteCommand(ac); String[] name = {"tastybento"}; when(pm.getUUID(Mockito.any())).thenReturn(null); - assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + assertFalse(itl.canExecute(user, itl.getLabel(), Arrays.asList(name))); Mockito.verify(user).sendMessage("general.errors.unknown-player", "[name]", name[0]); } @@ -153,7 +153,7 @@ public class AdminDeleteCommandTest { String[] name = {"tastybento"}; when(pm.getUUID(Mockito.any())).thenReturn(notUUID); when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); - assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + assertFalse(itl.canExecute(user, itl.getLabel(), Arrays.asList(name))); Mockito.verify(user).sendMessage(Mockito.eq("general.errors.player-has-no-island")); } @@ -167,7 +167,7 @@ public class AdminDeleteCommandTest { String[] name = {"tastybento"}; when(pm.getUUID(Mockito.any())).thenReturn(notUUID); AdminDeleteCommand itl = new AdminDeleteCommand(ac); - assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); + assertFalse(itl.canExecute(user, itl.getLabel(), Arrays.asList(name))); Mockito.verify(user).sendMessage("commands.admin.delete.cannot-delete-owner"); }