From b6a69d0c90729d4e2fac792369e9cc557d67b309 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 1 Mar 2021 10:42:08 -0800 Subject: [PATCH] Switch to island homes from player homes. (#1689) * Switch to island homes from player homes. Stores home locations and max homes in the Island object. Adds commands required to manage home names, specifically rename and delete. I did not add list as there is tab complete on island go, but it may be required. --- .../admin/conversations/NamePrompt.java | 48 ++ .../island/CustomIslandMultiHomeHelp.java | 47 -- .../commands/island/DefaultPlayerCommand.java | 6 +- .../island/IslandDeletehomeCommand.java | 81 ++++ .../api/commands/island/IslandGoCommand.java | 37 +- .../island/IslandRenamehomeCommand.java | 85 ++++ .../commands/island/IslandSethomeCommand.java | 61 ++- .../bentobox/database/objects/Island.java | 134 +++++- .../bentobox/listeners/JoinLeaveListener.java | 1 + .../worldsettings/IslandRespawnListener.java | 14 +- .../bentobox/managers/IslandsManager.java | 430 ++++++++++++------ .../util/teleport/SafeSpotTeleport.java | 29 +- src/main/resources/locales/en-US.yml | 17 +- .../island/CustomIslandMultiHomeHelpTest.java | 203 --------- .../island/IslandSethomeCommandTest.java | 84 ++-- .../IslandRespawnListenerTest.java | 2 +- .../bentobox/managers/IslandsManagerTest.java | 160 +++++-- .../teleport/SafeSpotTeleportBuilderTest.java | 2 +- .../util/teleport/SafeSpotTeleportTest.java | 153 ------- 19 files changed, 891 insertions(+), 703 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java delete mode 100644 src/main/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelp.java create mode 100644 src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java create mode 100644 src/main/java/world/bentobox/bentobox/api/commands/island/IslandRenamehomeCommand.java delete mode 100644 src/test/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelpTest.java delete mode 100644 src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java new file mode 100644 index 000000000..62d2055d1 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/conversations/NamePrompt.java @@ -0,0 +1,48 @@ +package world.bentobox.bentobox.api.commands.admin.conversations; + +import org.bukkit.Bukkit; +import org.bukkit.conversations.ConversationContext; +import org.bukkit.conversations.Prompt; +import org.bukkit.conversations.StringPrompt; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Renames a home + * @author tastybento + * + */ +public class NamePrompt extends StringPrompt { + + private @NonNull final Island island; + private @NonNull final User user; + private final String oldName; + private final BentoBox plugin; + + public NamePrompt(BentoBox plugin, @NonNull Island island, User user, String oldName) { + this.plugin = plugin; + this.island = island; + this.user = user; + this.oldName = oldName; + } + + @Override + public String getPromptText(ConversationContext context) { + return user.getTranslation("commands.island.renamehome.enter-new-name"); + } + + @Override + public Prompt acceptInput(ConversationContext context, String input) { + if (island.renameHome(oldName, input)) { + plugin.getIslands().save(island); + Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("general.success")); + } else { + Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("commands.island.renamehome.already-exists")); + } + return Prompt.END_OF_CONVERSATION; + } + +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelp.java b/src/main/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelp.java deleted file mode 100644 index 111a4e0d6..000000000 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelp.java +++ /dev/null @@ -1,47 +0,0 @@ -package world.bentobox.bentobox.api.commands.island; - -import java.util.List; - -import world.bentobox.bentobox.api.commands.CompositeCommand; -import world.bentobox.bentobox.api.user.User; - -/** - * This is a custom help for the /island go and /island sethome commands. It overrides the default help sub command. - * The number of homes can change depending on the player's permissions and config.yml settings. - * This is an example of a custom help as much as anything. - * - * @author tastybento - */ -public class CustomIslandMultiHomeHelp extends CompositeCommand { - - public CustomIslandMultiHomeHelp(CompositeCommand parent) { - super(parent, "help"); - } - - @Override - public void setup() { - setOnlyPlayer(true); - // Inherit parameters from the respective parent class - in this case, only /island go and /island sethome - setParametersHelp(parent.getParameters()); - setDescription(parent.getDescription()); - inheritPermission(); - } - - @Override - public boolean canExecute(User user, String label, List args) { - return user.isPlayer() && user.hasPermission(getPermission()); - } - - @Override - public boolean execute(User user, String label, List args) { - // Get elements - String usage = parent.getUsage().isEmpty() ? "" : user.getTranslation(parent.getUsage()); - int maxHomes = user.getPermissionValue(getPermissionPrefix() + "island.maxhomes", getIWM().getMaxHomes(getWorld())); - String params = maxHomes > 1 ? user.getTranslation(getParameters()) : ""; - String desc = getDescription().isEmpty() ? "" : user.getTranslation(getDescription()); - user.sendMessage("commands.help.syntax", "[usage]", usage, "[parameters]", params, "[description]", desc); - return true; - } - -} - diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommand.java index d3844570d..0517c8b8e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommand.java @@ -56,7 +56,6 @@ public abstract class DefaultPlayerCommand extends CompositeCommand { // Settings related commands new IslandSettingsCommand(this); - new IslandSethomeCommand(this); new IslandSetnameCommand(this); new IslandResetnameCommand(this); new IslandLanguageCommand(this); @@ -74,6 +73,11 @@ public abstract class DefaultPlayerCommand extends CompositeCommand { // Team commands new IslandTeamCommand(this); + + // Home commands + new IslandSethomeCommand(this); + new IslandDeletehomeCommand(this); + new IslandRenamehomeCommand(this); } 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 new file mode 100644 index 000000000..761aa5358 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java @@ -0,0 +1,81 @@ +package world.bentobox.bentobox.api.commands.island; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.commands.ConfirmableCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; + +/** + * Deletes a home + * @author tastybento + * + */ +public class IslandDeletehomeCommand extends ConfirmableCommand { + + private @Nullable Island island; + + public IslandDeletehomeCommand(CompositeCommand islandCommand) { + super(islandCommand, "deletehome"); + } + + @Override + public void setup() { + setPermission("island.deletehome"); + setOnlyPlayer(true); + setParametersHelp("commands.island.deletehome.parameters"); + setDescription("commands.island.deletehome.description"); + } + + @Override + public boolean canExecute(User user, String label, List args) { + if (args.isEmpty()) { + this.showHelp(this, user); + return false; + } + island = getIslands().getIsland(getWorld(), user); + // Check island + if (island == null) { + user.sendMessage("general.errors.no-island"); + return false; + } + // Check if the name is known + if (!getIslands().isHomeLocation(island, String.join(" ", args))) { + user.sendMessage("commands.island.go.unknown-home"); + user.sendMessage("commands.island.sethome.homes-are"); + island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("home-list-syntax", TextVariables.NAME, s)); + return false; + } + return true; + } + + @Override + public boolean execute(User user, String label, List args) { + this.askConfirmation(user, () -> delete(island, user, String.join(" ", args))); + return true; + } + + + private void delete(Island island, User user, String name) { + island.removeHome(name); + user.sendMessage("general.success"); + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + if (island != null) { + return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg)); + } else { + return Optional.empty(); + } + } +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java index 6e40072d1..736f85eab 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java @@ -1,15 +1,17 @@ package world.bentobox.bentobox.api.commands.island; +import java.util.ArrayList; import java.util.Collections; import java.util.List; - -import org.apache.commons.lang.math.NumberUtils; +import java.util.Optional; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.DelayedTeleportCommand; +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.lists.Flags; +import world.bentobox.bentobox.util.Util; /** * @author tastybento @@ -24,8 +26,8 @@ public class IslandGoCommand extends DelayedTeleportCommand { public void setup() { setPermission("island.home"); setOnlyPlayer(true); + setParametersHelp("commands.island.go.parameters"); setDescription("commands.island.go.description"); - new CustomIslandMultiHomeHelp(this); } @Override @@ -47,21 +49,32 @@ public class IslandGoCommand extends DelayedTeleportCommand { user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference()); return false; } + if (!args.isEmpty()) { + if (!getIslands().isHomeLocation(island, String.join(" ", args))) { + user.sendMessage("commands.island.go.unknown-home"); + user.sendMessage("commands.island.sethome.homes-are"); + island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)); + return false; + } + } return true; } @Override public boolean execute(User user, String label, List args) { - if (!args.isEmpty() && NumberUtils.isDigits(args.get(0))) { - int homeValue = Integer.parseInt(args.get(0)); - int maxHomes = user.getPermissionValue(getPermissionPrefix() + "island.maxhomes", getIWM().getMaxHomes(getWorld())); - if (homeValue > 1 && homeValue <= maxHomes) { - this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), homeValue)); - return true; - } - } - this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer())); + this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), String.join(" ", args))); return true; } + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + if (island != null) { + return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg)); + } else { + return Optional.empty(); + } + } + } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandRenamehomeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandRenamehomeCommand.java new file mode 100644 index 000000000..ebd96d27c --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandRenamehomeCommand.java @@ -0,0 +1,85 @@ +package world.bentobox.bentobox.api.commands.island; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.bukkit.conversations.ConversationFactory; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.commands.ConfirmableCommand; +import world.bentobox.bentobox.api.commands.admin.conversations.NamePrompt; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; + +/** + * Renames a home + * @author tastybento + * + */ +public class IslandRenamehomeCommand extends ConfirmableCommand { + + private @Nullable Island island; + + public IslandRenamehomeCommand(CompositeCommand islandCommand) { + super(islandCommand, "renamehome"); + } + + @Override + public void setup() { + setPermission("island.renamehome"); + setOnlyPlayer(true); + setParametersHelp("commands.island.renamehome.parameters"); + setDescription("commands.island.renamehome.description"); + } + + @Override + public boolean canExecute(User user, String label, List args) { + if (args.isEmpty()) { + this.showHelp(this, user); + return false; + } + island = getIslands().getIsland(getWorld(), user); + // Check island + if (island == null) { + user.sendMessage("general.errors.no-island"); + return false; + } + // Check if the name is known + if (!getIslands().isHomeLocation(island, String.join(" ", args))) { + user.sendMessage("commands.island.go.unknown-home"); + user.sendMessage("commands.island.sethome.homes-are"); + island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)); + this.showHelp(this, user); + return false; + } + return true; + } + + @Override + public boolean execute(User user, String label, List args) { + new ConversationFactory(BentoBox.getInstance()) + .withModality(true) + .withLocalEcho(false) + .withTimeout(90) + .withFirstPrompt(new NamePrompt(getPlugin(), island, user, String.join(" ", args))) + .buildConversation(user.getPlayer()).begin(); + return true; + } + + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + Island island = getIslands().getIsland(getWorld(), user.getUniqueId()); + if (island != null) { + return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg)); + } else { + return Optional.empty(); + } + } +} diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommand.java index db0d72a72..2eb31135b 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommand.java @@ -2,13 +2,18 @@ package world.bentobox.bentobox.api.commands.island; import java.util.List; +import org.eclipse.jdt.annotation.Nullable; + import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; public class IslandSethomeCommand extends ConfirmableCommand { + private @Nullable Island island; + public IslandSethomeCommand(CompositeCommand islandCommand) { super(islandCommand, "sethome"); } @@ -18,54 +23,34 @@ public class IslandSethomeCommand extends ConfirmableCommand { setPermission("island.sethome"); setOnlyPlayer(true); setDescription("commands.island.sethome.description"); - new CustomIslandMultiHomeHelp(this); } @Override public boolean canExecute(User user, String label, List args) { + island = getIslands().getIsland(getWorld(), user); // Check island - if (!getPlugin().getIslands().hasIsland(getWorld(), user) && !getPlugin().getIslands().inTeam(getWorld(), user.getUniqueId())) { + if (island == null || island.getOwner() == null) { user.sendMessage("general.errors.no-island"); return false; } - if (!getPlugin().getIslands().locationIsOnIsland(user.getPlayer(), user.getLocation())) { + if (!island.onIsland(user.getLocation())) { user.sendMessage("commands.island.sethome.must-be-on-your-island"); return false; } + // Check number of homes + int maxHomes = getIslands().getMaxHomes(island); + if (getIslands().getNumberOfHomesIfAdded(island, String.join(" ", args)) > maxHomes) { + user.sendMessage("commands.island.sethome.too-many-homes", TextVariables.NUMBER, String.valueOf(island.getMaxHomes())); + user.sendMessage("commands.island.sethome.homes-are"); + island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("home-list-syntax", TextVariables.NAME, s)); + return false; + } return true; } @Override public boolean execute(User user, String label, List args) { - if (args.isEmpty()) { - // island sethome - return setHome(user, 1); - } else { - // Dynamic home sizes with permissions - int maxHomes = user.getPermissionValue(getPermissionPrefix() + "island.maxhomes", getIWM().getMaxHomes(getWorld())); - if (maxHomes > 1) { - // Check the number given is a number - int number; - try { - number = Integer.parseInt(args.get(0)); - if (number < 1 || number > maxHomes) { - user.sendMessage("commands.island.sethome.num-homes", TextVariables.NUMBER, String.valueOf(maxHomes)); - return false; - } else { - return setHome(user, number); - } - } catch (Exception e) { - user.sendMessage("commands.island.sethome.num-homes", TextVariables.NUMBER, String.valueOf(maxHomes)); - return false; - } - } else { - user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, this.getPermissionPrefix() + "island.maxhomes.[number]"); - return false; - } - } - } - - private boolean setHome(User user, int number) { + String number = String.join(" ", args); // Check if the player is in the Nether if (getIWM().isNether(user.getWorld())) { // Check if he is (not) allowed to set his home here @@ -99,10 +84,16 @@ public class IslandSethomeCommand extends ConfirmableCommand { return true; } - private void doSetHome(User user, int number) { + private void doSetHome(User user, String name) { // Define a runnable as we will be using it often in the code below. - getPlugin().getPlayers().setHomeLocation(user, user.getLocation(), number); + getIslands().setHomeLocation(user, user.getLocation(), name); user.sendMessage("commands.island.sethome.home-set"); - + if (island.getHomes().size() > 1) { + user.sendMessage("commands.island.sethome.homes-are"); + island + .getHomes() + .keySet() + .stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)); + } } } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index dd6bd409b..9b012dc66 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -24,6 +24,7 @@ import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.NotNull; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; @@ -203,6 +204,19 @@ public class Island implements DataObject, MetaDataAble { @Expose private Map metaData; + /** + * Island homes. Replaces player homes + * @since 1.16.0 + */ + @Expose + private Map homes; + + /** + * The maximum number of homes allowed on this island. If null, then the world default is used. + */ + @Expose + private Integer maxHomes; + /* * *************************** Constructors ****************************** */ @@ -1419,12 +1433,99 @@ public class Island implements DataObject, MetaDataAble { setChanged(); } + /** + * @return the homes + * @since 1.16.0 + */ + @NotNull + public Map getHomes() { + if (homes == null) { + homes = new HashMap<>(); + } + return homes; + } + + /** + * @return the homes + * @since 1.16.0 + */ + @Nullable + public Location getHome(String name) { + return getHomes().get(name.toLowerCase()); + } + + /** + * @param homes the homes to set + * @since 1.16.0 + */ + public void setHomes(Map homes) { + this.homes = homes; + setChanged(); + } + + /** + * @param homes the homes to set + * @since 1.16.0 + */ + public void addHome(String name, Location location) { + getHomes().put(name.toLowerCase(), location); + setChanged(); + } + + /** + * Remove a named home from this island + * @param name - home name to remove + * @return true if home removed successfully + * @since 1.16.0 + */ + public boolean removeHome(String name) { + setChanged(); + return getHomes().remove(name.toLowerCase()) != null; + } + + /** + * Rename a home + * @param oldName - old name of home + * @param newName - new name of home + * @return true if successful, false if oldName does not exist, already exists + * @since 1.16.0 + */ + public boolean renameHome(String oldName, String newName) { + if (getHomes().containsKey(oldName.toLowerCase()) && !getHomes().containsKey(newName.toLowerCase())) { + this.addHome(newName, this.getHome(oldName)); + this.removeHome(oldName); + return true; + } + return false; + } + + /** + * @return the maxHomes. If null, then the world default should be used. + * @since 1.16.0 + */ + @Nullable + public Integer getMaxHomes() { + return maxHomes; + } + + /** + * @param maxHomes the maxHomes to set. If null then the world default will be used. + * @since 1.16.0 + */ + public void setMaxHomes(@Nullable Integer maxHomes) { + this.maxHomes = maxHomes; + setChanged(); + } + /** * @return the maxMembers * @since 1.16.0 */ public Map getMaxMembers() { - return maxMembers == null ? new HashMap<>() : maxMembers; + if (maxMembers == null) { + maxMembers = new HashMap<>(); + } + return maxMembers; } /** @@ -1433,6 +1534,7 @@ public class Island implements DataObject, MetaDataAble { */ public void setMaxMembers(Map maxMembers) { this.maxMembers = maxMembers; + setChanged(); } /** @@ -1457,26 +1559,22 @@ public class Island implements DataObject, MetaDataAble { getMaxMembers().put(rank, maxMembers); } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ @Override public String toString() { - return "Island [changed=" + changed + ", deleted=" + deleted + ", " - + (uniqueId != null ? "uniqueId=" + uniqueId + ", " : "") - + (center != null ? "center=" + center + ", " : "") - + (location != null ? "location=" + location + ", " : "") + "range=" + range + ", protectionRange=" - + protectionRange + ", maxEverProtectionRange=" + maxEverProtectionRange + ", " - + (world != null ? "world=" + world + ", " : "") - + (gameMode != null ? "gameMode=" + gameMode + ", " : "") + (name != null ? "name=" + name + ", " : "") - + "createdDate=" + createdDate + ", updatedDate=" + updatedDate + ", " - + (owner != null ? "owner=" + owner + ", " : "") + (members != null ? "members=" + members + ", " : "") - + (maxMembers != null ? "maxMembers=" + maxMembers + ", " : "") + "spawn=" + spawn + ", purgeProtected=" - + purgeProtected + ", " + (flags != null ? "flags=" + flags + ", " : "") - + (history != null ? "history=" + history + ", " : "") + "levelHandicap=" + levelHandicap + ", " - + (spawnPoint != null ? "spawnPoint=" + spawnPoint + ", " : "") + "doNotLoad=" + doNotLoad + ", " - + (cooldowns != null ? "cooldowns=" + cooldowns + ", " : "") - + (commandRanks != null ? "commandRanks=" + commandRanks + ", " : "") - + (reserved != null ? "reserved=" + reserved + ", " : "") - + (metaData != null ? "metaData=" + metaData : "") + "]"; + return "Island [changed=" + changed + ", deleted=" + deleted + ", uniqueId=" + uniqueId + ", center=" + center + + ", location=" + location + ", range=" + range + ", protectionRange=" + protectionRange + + ", maxEverProtectionRange=" + maxEverProtectionRange + ", world=" + world + ", gameMode=" + gameMode + + ", name=" + name + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + ", owner=" + + owner + ", members=" + members + ", maxMembers=" + maxMembers + ", spawn=" + spawn + + ", purgeProtected=" + purgeProtected + ", flags=" + flags + ", history=" + history + + ", levelHandicap=" + levelHandicap + ", spawnPoint=" + spawnPoint + ", doNotLoad=" + doNotLoad + + ", cooldowns=" + cooldowns + ", commandRanks=" + commandRanks + ", reserved=" + reserved + + ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]"; } + } diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 035747b32..29882ce21 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -100,6 +100,7 @@ public class JoinLeaveListener implements Listener { plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); + plugin.getIslands().getMaxHomes(i); }); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListener.java index ec4e5f944..0a8d411d7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListener.java @@ -22,7 +22,7 @@ import world.bentobox.bentobox.util.Util; * */ public class IslandRespawnListener extends FlagListener { - + private final Map respawn = new HashMap<>(); /** @@ -41,10 +41,10 @@ public class IslandRespawnListener extends FlagListener { if (!getIslands().hasIsland(world, e.getEntity().getUniqueId()) && !getIslands().inTeam(world, e.getEntity().getUniqueId())) { return; // doesn't have an island in this world } - + respawn.put(e.getEntity().getUniqueId(), world.getUID()); } - + /** * Place players back on their island if respawn on island is true and active * @param e - event @@ -55,18 +55,18 @@ public class IslandRespawnListener extends FlagListener { if (worldUUID == null) { return; // no respawn world set } - + final World world = e.getPlayer().getServer().getWorld(worldUUID); if (world == null) { return; // world no longer available } - - final Location respawnLocation = getIslands().getSafeHomeLocation(Util.getWorld(world), User.getInstance(e.getPlayer().getUniqueId()), 1); + + final Location respawnLocation = getIslands().getSafeHomeLocation(Util.getWorld(world), User.getInstance(e.getPlayer().getUniqueId()), ""); if (respawnLocation != null) { e.setRespawnLocation(respawnLocation); } // Run respawn commands, if any Util.runCommands(User.getInstance(e.getPlayer()), getIWM().getOnRespawnCommands(world), "respawn"); } - + } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 4c4ccb602..bd23e66fb 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -9,6 +9,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Queue; @@ -391,7 +392,7 @@ public class IslandsManager { * @return Island or null */ @Nullable - public Island getIsland(@NonNull World world, @NonNull UUID uuid){ + public Island getIsland(@NonNull World world, @NonNull UUID uuid) { return islandCache.get(world, uuid); } @@ -541,6 +542,36 @@ public class IslandsManager { public void setMaxMembers(@NonNull Island island, int rank, Integer maxMembers) { island.setMaxMembers(rank, maxMembers); } + + /** + * Get the maximum number of homes allowed on this island. Will be updated with the owner's permission settings if + * they exist and the owner is online + * @param island - island + * @return maximum number of homes + * @since 1.16.0 + */ + public int getMaxHomes(@NonNull Island island) { + int islandMax = island.getMaxHomes() == null ? plugin.getIWM().getMaxHomes(island.getWorld()) : island.getMaxHomes(); + // Update based on owner permissions if online + if (Bukkit.getPlayer(island.getOwner()) != null) { + User owner = User.getInstance(island.getOwner()); + islandMax = owner.getPermissionValue(plugin.getIWM().getPermissionPrefix(island.getWorld()) + + "island.maxhomes", islandMax); + } + island.setMaxHomes(islandMax == plugin.getIWM().getMaxHomes(island.getWorld()) ? null : islandMax); + this.save(island); + return islandMax; + } + + /** + * Set the maximum numbber of homes allowed on this island + * @param island - island + * @param maxHomes - max number of homes allowed, or null if the world default should be used + * @since 1.16.0 + */ + public void setMaxHomes(@NonNull Island island, @Nullable Integer maxHomes) { + island.setMaxHomes(maxHomes); + } /** * Returns the island at the location or Optional empty if there is none. @@ -558,11 +589,11 @@ public class IslandsManager { * Get a safe home location using async chunk loading and set the home location * @param world - world * @param user - user - * @param number - number number + * @param name - home name * @return CompletableFuture with the location found, or null * @since 1.14.0 */ - public CompletableFuture getAsyncSafeHomeLocation(@NonNull World world, @NonNull User user, int number) { + private CompletableFuture getAsyncSafeHomeLocation(@NonNull World world, @NonNull User user, String name) { CompletableFuture result = new CompletableFuture<>(); // Check if the world is a gamemode world and the player has an island Location islandLoc = getIslandLocation(world, user.getUniqueId()); @@ -570,10 +601,10 @@ public class IslandsManager { result.complete(null); return result; } - // Try the numbered home location first - Location defaultHome = plugin.getPlayers().getHomeLocation(world, user, 1); - Location numberedHome = plugin.getPlayers().getHomeLocation(world, user, number); - Location l = numberedHome != null ? numberedHome : defaultHome; + // Try the home location first + Location defaultHome = getHomeLocation(world, user); + Location namedHome = getHomeLocation(world, user, name); + Location l = namedHome != null ? namedHome : defaultHome; if (l != null) { Util.getChunkAtAsync(l).thenRun(() -> { // Check if it is safe @@ -585,25 +616,25 @@ public class IslandsManager { Location lPlusOne = l.clone().add(new Vector(0, 1, 0)); if (isSafeLocation(lPlusOne)) { // Adjust the home location accordingly - plugin.getPlayers().setHomeLocation(user, lPlusOne, number); + setHomeLocation(user, lPlusOne, name); result.complete(lPlusOne); return; } // Try island - tryIsland(result, islandLoc, user, number); + tryIsland(result, islandLoc, user, name); }); return result; } // Try island - tryIsland(result, islandLoc, user, number); + tryIsland(result, islandLoc, user, name); return result; } - private void tryIsland(CompletableFuture result, Location islandLoc, @NonNull User user, int number) { + private void tryIsland(CompletableFuture result, Location islandLoc, @NonNull User user, String number) { Util.getChunkAtAsync(islandLoc).thenRun(() -> { World w = islandLoc.getWorld(); if (isSafeLocation(islandLoc)) { - plugin.getPlayers().setHomeLocation(user, islandLoc, number); + setHomeLocation(user, islandLoc, number); result.complete(islandLoc.clone().add(new Vector(0.5D,0,0.5D))); return; } else { @@ -611,14 +642,14 @@ public class IslandsManager { // Try the default location Location dl = islandLoc.clone().add(new Vector(0.5D, 5D, 2.5D)); if (isSafeLocation(dl)) { - plugin.getPlayers().setHomeLocation(user, dl, number); + setHomeLocation(user, dl, number); result.complete(dl); return; } // Try just above the bedrock dl = islandLoc.clone().add(new Vector(0.5D, 5D, 0.5D)); if (isSafeLocation(dl)) { - plugin.getPlayers().setHomeLocation(user, dl, number); + setHomeLocation(user, dl, number); result.complete(dl); return; } @@ -626,7 +657,7 @@ public class IslandsManager { for (int y = islandLoc.getBlockY(); y < w.getMaxHeight(); y++) { dl = new Location(w, islandLoc.getX() + 0.5D, y, islandLoc.getZ() + 0.5D); if (isSafeLocation(dl)) { - plugin.getPlayers().setHomeLocation(user, dl, number); + setHomeLocation(user, dl, number); result.complete(dl); return; } @@ -643,22 +674,20 @@ public class IslandsManager { * * @param world - world to check, not null * @param user - the player, not null - * @param number - a number - starting home location, e.g. 1 + * @param name - named home location. Blank means default. * @return Location of a safe teleport spot or {@code null} if one cannot be found or if the world is not an island world. */ - public Location getSafeHomeLocation(@NonNull World world, @NonNull User user, int number) { + public Location getSafeHomeLocation(@NonNull World world, @NonNull User user, String name) { // Check if the world is a gamemode world if (!plugin.getIWM().inWorld(world)) { return null; } - - // Try the numbered home location first - Location l = plugin.getPlayers().getHomeLocation(world, user, number); - + // Try the named home location first + Location l = getHomeLocation(world, user, name); if (l == null) { // Get the default home, which may be null too, but that's okay - number = 1; - l = plugin.getPlayers().getHomeLocation(world, user, number); + name = ""; + l = getHomeLocation(world, user, name); } // Check if it is safe if (l != null) { @@ -670,7 +699,7 @@ public class IslandsManager { lPlusOne.add(new Vector(0, 1, 0)); if (isSafeLocation(lPlusOne)) { // Adjust the home location accordingly - plugin.getPlayers().setHomeLocation(user, lPlusOne, number); + setHomeLocation(user, lPlusOne, name); return lPlusOne; } } @@ -679,20 +708,20 @@ public class IslandsManager { if (inTeam(world, user.getUniqueId())) { l = getIslandLocation(world, user.getUniqueId()); if (l != null && isSafeLocation(l)) { - plugin.getPlayers().setHomeLocation(user, l, number); + setHomeLocation(user, l, name); return l; } else { // try owner's home - Location tlh = plugin.getPlayers().getHomeLocation(world, plugin.getIslands().getOwner(world, user.getUniqueId())); + Location tlh = getHomeLocation(world, getOwner(world, user.getUniqueId())); if (tlh != null && isSafeLocation(tlh)) { - plugin.getPlayers().setHomeLocation(user, tlh, number); + setHomeLocation(user, tlh, name); return tlh; } } } else { l = getIslandLocation(world, user.getUniqueId()); if (l != null && isSafeLocation(l)) { - plugin.getPlayers().setHomeLocation(user, l, number); + setHomeLocation(user, l, name); return l.clone().add(new Vector(0.5D,0,0.5D)); } } @@ -704,20 +733,20 @@ public class IslandsManager { // Try the default location Location dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 2.5D, 0F, 30F); if (isSafeLocation(dl)) { - plugin.getPlayers().setHomeLocation(user, dl, number); + setHomeLocation(user, dl, name); return dl; } // Try just above the bedrock dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 0.5D, 0F, 30F); if (isSafeLocation(dl)) { - plugin.getPlayers().setHomeLocation(user, dl, number); + setHomeLocation(user, dl, name); return dl; } // Try all the way up to the sky for (int y = l.getBlockY(); y < 255; y++) { final Location n = new Location(l.getWorld(), l.getX() + 0.5D, y, l.getZ() + 0.5D); if (isSafeLocation(n)) { - plugin.getPlayers().setHomeLocation(user, n, number); + setHomeLocation(user, n, name); return n; } } @@ -725,6 +754,210 @@ public class IslandsManager { return null; } + /** + * Sets a home location on user's island. Replaces previous location if the same name is used + * @param user - user + * @param location - location on island + * @param name - name of home, or blank for default home + * @return true if home location was set. False if this location is not on the island. + * @since 1.16.0 + */ + public boolean setHomeLocation(@NonNull User user, Location location, String name) { + return setHomeLocation(user.getUniqueId(), location, name); + } + + /** + * Sets a home location on user's island. Replaces previous location if the same name is used + * @param uuid - user uuid + * @param location - location on island + * @param name - name of home, or blank for default home + * @return true if home location was set. False if this location is not on the island. + * @since 1.16.0 + */ + public boolean setHomeLocation(@NonNull UUID uuid, Location location, String name) { + return setHomeLocation(this.getIsland(location.getWorld(), uuid), location, name); + } + + /** + * Set a default home location for user on their island + * @param uuid - user uuid + * @param location - location on island + * @return true if home location was set. False if this location is not on the island. + * @since 1.16.0 + */ + public boolean setHomeLocation(@NonNull UUID uuid, Location location) { + return setHomeLocation(uuid, location, ""); + } + + /** + * Set a home location for island + * @param island - island + * @param location - location + * @param name - name of home, or blank for default home + * @return true if home location was set. False if this location is not on the island. + * @since 1.16.0 + */ + public boolean setHomeLocation(@Nullable Island island, Location location, String name) { + if (island != null) { + island.addHome(name, location); + this.save(island); + return true; + } + return false; + } + + /** + * Get the home location for user in world + * @param world - world + * @param user - user + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@NonNull World world, @NonNull User user) { + return getHomeLocation(world, user, ""); + } + + /** + * Get the home location for player's UUID in world + * @param world - world + * @param uuid - uuid of player + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@NonNull World world, @NonNull UUID uuid) { + return getHomeLocation(world, uuid, ""); + } + + /** + * Get the named home location for user in world + * @param world - world + * @param user - user + * @param name - name of home, or blank for default + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@NonNull World world, @NonNull User user, String name) { + return getHomeLocation(world, user.getUniqueId(), name); + } + + /** + * Get the named home location for user in world + * @param world - world + * @param uuid - uuid of player + * @param name - name of home, or blank for default + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@NonNull World world, @NonNull UUID uuid, String name) { + // Migrate from player homes to island homes + Island island = this.getIsland(world, uuid); + migrateHomes(world, uuid, name, island); + return getHomeLocation(island, name); + } + + private void migrateHomes(@NonNull World world, @NonNull UUID uuid, String name, Island island) { + Map homes = plugin + .getPlayers() + .getHomeLocations(world, uuid); + if (homes.isEmpty()) { + // No migration required + return; + } + if (island.getOwner().equals(uuid)) { + // Owner + island.setHomes(homes.entrySet().stream().collect(Collectors.toMap(this::getHomeName, Map.Entry::getKey))); + plugin.getPlayers().clearHomeLocations(world, uuid); + } + } + + private String getHomeName(Entry e) { + // Home 1 has an empty name + if (e.getValue() == 1) { + return ""; + } + return String.valueOf(e.getValue()); + } + + /** + * Get the default home location for this island + * @param island - island + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@Nullable Island island) { + return getHomeLocation(island, ""); + } + + /** + * Get the named home location for this island + * @param island - island + * @param name - name of home, or blank for default + * @return home location + * @since 1.16.0 + */ + @NonNull + public Location getHomeLocation(@Nullable Island island, String name) { + return island == null ? null : island.getHome(name); + } + + /** + * Remove the named home location from this island + * @param island - island + * @param name - name of home, or blank for default + * @return true if successful, false if not + * @since 1.16.0 + */ + public boolean removeHomeLocation(@Nullable Island island, String name) { + return island == null ? false : island.removeHome(name); + } + + /** + * Rename a home + * @param island - island + * @param oldName - old name + * @param newName - new name + * @return true if successful, false if not + */ + public boolean renameHomeLocation(@Nullable Island island, String oldName, String newName) { + return island == null ? false : island.renameHome(oldName, newName); + } + + /** + * Get the all the home locations for this island + * @param island - island + * @return map of home locations with the name as the key + * @since 1.16.0 + */ + @NonNull + public Map getHomeLocations(@NonNull Island island) { + return island.getHomes(); + } + + /** + * Check if a home name exists or not + * @param island - island + * @param name - name being checked + * @return true if it exists or not + */ + public boolean isHomeLocation(@NonNull Island island, String name) { + return island.getHomes().containsKey(name.toLowerCase()); + } + + /** + * Get the number of homes on this island if this home were added + * @param island - island + * @param name - name + * @return number of homes after adding this one + */ + public int getNumberOfHomesIfAdded(@NonNull Island island, String name) { + return isHomeLocation(island, name) ? getHomeLocations(island).size() : getHomeLocations(island).size() + 1; + } + /** * Gets the island that is defined as spawn in this world * @param world world @@ -775,47 +1008,6 @@ public class IslandsManager { return islandCache.hasIsland(world, uuid); } - /** - * This teleports player to their island. If not safe place can be found - * then the player is sent to spawn via /spawn command - * - * @param world - world to check - * @param player - the player - * @deprecated as of 1.14.0. Use homeTeleportAsync instead. - */ - @Deprecated - public void homeTeleport(@NonNull World world, @NonNull Player player) { - homeTeleport(world, player, 1, false); - } - - /** - * Teleport player to a home location. If one cannot be found a search is done to - * find a safe place. - * - * @param world - world to check - * @param player - the player - * @param number - a number - home location to do to - * @deprecated as of 1.14.0. Use homeTeleportAsync instead. - */ - @Deprecated - public void homeTeleport(@NonNull World world, @NonNull Player player, int number) { - homeTeleport(world, player, number, false); - } - - /** - * This teleports player to their island. If not safe place can be found - * then the player is sent to spawn via /spawn command - * - * @param world - world to check - * @param player - the player - * @param newIsland - true if this is a new island teleport - * @deprecated as of 1.14.0. Use homeTeleportAsync instead. - */ - @Deprecated - public void homeTeleport(@NonNull World world, @NonNull Player player, boolean newIsland) { - homeTeleport(world, player, 1, newIsland); - } - /** * This teleports player to their island. If not safe place can be found * then the player is sent to spawn via /spawn command @@ -826,7 +1018,7 @@ public class IslandsManager { * @since 1.14.0 */ public CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player) { - return homeTeleportAsync(world, player, 1, false); + return homeTeleportAsync(world, player, "", false); } /** @@ -838,13 +1030,29 @@ public class IslandsManager { * @param number - a number - home location to do to * @return CompletableFuture true if successful, false if not * @since 1.14.0 + * @deprecated Use {@link #homeTeleportAsync(World, Player, String)} */ + @Deprecated public CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player, int number) { - return homeTeleportAsync(world, player, number, false); + return homeTeleportAsync(world, player, String.valueOf(number), false); } /** - * This teleports player to their island. If not safe place can be found + * Teleport player to a home location. If one cannot be found a search is done to + * find a safe place. + * + * @param world - world to check + * @param player - the player + * @param name - a named home location. Blank means default. + * @return CompletableFuture true if successful, false if not + * @since 1.16.0 + */ + public CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player, String name) { + return homeTeleportAsync(world, player, name, false); + } + + /** + * This teleports player to their island. If no safe place can be found * then the player is sent to spawn via /spawn command * * @param world - world to check @@ -854,11 +1062,11 @@ public class IslandsManager { * @since 1.14.0 */ public CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player, boolean newIsland) { - return homeTeleportAsync(world, player, 1, newIsland); + return homeTeleportAsync(world, player, "", newIsland); } - private CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player, int number, boolean newIsland) { + private CompletableFuture homeTeleportAsync(@NonNull World world, @NonNull Player player, String name, boolean newIsland) { CompletableFuture result = new CompletableFuture<>(); User user = User.getInstance(player); user.sendMessage("commands.island.go.teleport"); @@ -875,26 +1083,27 @@ public class IslandsManager { player.updateInventory(); } } - this.getAsyncSafeHomeLocation(world, user, number).thenAccept(home -> { + this.getAsyncSafeHomeLocation(world, user, name).thenAccept(home -> { + Island island = getIsland(world, user); if (home == null) { // Try to fix this teleport location and teleport the player if possible new SafeSpotTeleport.Builder(plugin) .entity(player) - .island(plugin.getIslands().getIsland(world, user)) - .homeNumber(number) - .thenRun(() -> teleported(world, user, number, newIsland)) + .island(island) + .homeName(name) + .thenRun(() -> teleported(world, user, name, newIsland)) .buildFuture() .thenAccept(result::complete); return; } // Add home - if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) { - plugin.getPlayers().setHomeLocation(player.getUniqueId(), home); + if (getHomeLocations(island).isEmpty()) { + setHomeLocation(player.getUniqueId(), home); } PaperLib.teleportAsync(player, home).thenAccept(b -> { // Only run the commands if the player is successfully teleported if (Boolean.TRUE.equals(b)) { - teleported(world, user, number, newIsland); + teleported(world, user, name, newIsland); result.complete(true); } else { result.complete(false); @@ -905,56 +1114,9 @@ public class IslandsManager { return result; } - - /** - * Teleport player to a home location. If one cannot be found a search is done to - * find a safe place. - * - * @param world - world to check - * @param player - the player - * @param number - a number - home location to do to - * @param newIsland - true if this is a new island teleport - */ - private void homeTeleport(@NonNull World world, @NonNull Player player, int number, boolean newIsland) { - User user = User.getInstance(player); - user.sendMessage("commands.island.go.teleport"); - Location home = getSafeHomeLocation(world, user, number); - // 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) { - player.leaveVehicle(); - // Remove the boat so they don't lie around everywhere - boat.remove(); - player.getInventory().addItem(new ItemStack(TREE_TO_BOAT.getOrDefault(((Boat) boat).getWoodType(), Material.OAK_BOAT))); - player.updateInventory(); - } - } - if (home == null) { - // Try to fix this teleport location and teleport the player if possible - new SafeSpotTeleport.Builder(plugin) - .entity(player) - .island(plugin.getIslands().getIsland(world, user)) - .homeNumber(number) - .thenRun(() -> teleported(world, user, number, newIsland)) - .build(); - return; - } - // Add home - if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) { - plugin.getPlayers().setHomeLocation(player.getUniqueId(), home); - } - PaperLib.teleportAsync(player, home).thenAccept(b -> { - // Only run the commands if the player is successfully teleported - if (Boolean.TRUE.equals(b)) teleported(world, user, number, newIsland); - }); - } - - private void teleported(World world, User user, int number, boolean newIsland) { - if (number > 1) { - user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, String.valueOf(number)); + private void teleported(World world, User user, String name, boolean newIsland) { + if (!name.isEmpty()) { + user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, name); } // If this is a new island, then run commands and do resets if (newIsland) { diff --git a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java index c586a4483..84594d499 100644 --- a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java +++ b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java @@ -51,6 +51,7 @@ public class SafeSpotTeleport { private List> chunksToScan; private final Runnable runnable; private final CompletableFuture result; + private final String homeName; /** * Teleports and entity to a safe spot on island @@ -62,6 +63,7 @@ public class SafeSpotTeleport { this.location = builder.getLocation(); this.portal = builder.isPortal(); this.homeNumber = builder.getHomeNumber(); + this.homeName = builder.getHomeName(); this.runnable = builder.getRunnable(); this.result = builder.getResult(); @@ -241,9 +243,9 @@ public class SafeSpotTeleport { task.cancel(); // Return to main thread and teleport the player Bukkit.getScheduler().runTask(plugin, () -> { - if (!portal && entity instanceof Player && homeNumber > 0) { + if (!portal && entity instanceof Player && (homeNumber > 0 || !homeName.isEmpty())) { // Set home if so marked - plugin.getPlayers().setHomeLocation(User.getInstance(entity), loc, homeNumber); + plugin.getIslands().setHomeLocation(User.getInstance(entity), loc, homeName); } Util.teleportAsync(entity, loc).thenRun(() -> { if (runnable != null) Bukkit.getScheduler().runTask(plugin, runnable); @@ -293,6 +295,7 @@ public class SafeSpotTeleport { private final BentoBox plugin; private Entity entity; private int homeNumber = 0; + private String homeName; private boolean portal = false; private String failureMessage = ""; private Location location; @@ -327,12 +330,25 @@ public class SafeSpotTeleport { * Set the home number to this number * @param homeNumber home number * @return Builder + * @deprecated use {@link #homeName} */ + @Deprecated public Builder homeNumber(int homeNumber) { this.homeNumber = homeNumber; return this; } + /** + * Set the home name + * @param homeName - home name + * @return Builder + * @Since 1.16.0 + */ + public Builder homeName(String homeName) { + this.homeName = homeName; + return this; + } + /** * This is a portal teleportation * @return Builder @@ -428,6 +444,13 @@ public class SafeSpotTeleport { return homeNumber; } + /** + * @return the homeName + */ + public String getHomeName() { + return homeName; + } + /** * @return the portal */ @@ -463,5 +486,7 @@ public class SafeSpotTeleport { public CompletableFuture getResult() { return result; } + + } } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index a07be2d8a..50b5e982a 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -466,10 +466,11 @@ commands: about: description: "display licence details" go: - parameters: "[home number]" + parameters: "[home name]" description: "teleport you to your island" teleport: "&a Teleporting you to your island." - teleported: "&a Teleported you to home &e #[number]." + teleported: "&a Teleported you to home &e [number]." + unknown-home: "&c Unknown home name!" help: description: "the main island command" spawn: @@ -492,6 +493,9 @@ commands: unknown-blueprint: "&c That blueprint has not been loaded yet." on-first-login: "&a Welcome! We will start preparing your island in a few seconds." you-can-teleport-to-your-island: "&a You can teleport to your island when you want." + deletehome: + description: "delete a home location" + parameters: "[home name]" info: description: "display info about your island or the player's island" parameters: "" @@ -518,8 +522,10 @@ commands: sethome: description: "set your home teleport point" must-be-on-your-island: "&c You must be on your island to set home!" - num-homes: "&c Homes can be 1 to [number]." + too-many-homes: "&c Cannot set - your island is at the maximum of [number] homes." home-set: "&6 Your island home has been set to your current location." + homes-are: "&6 Island homes are:" + home-list-syntax: "&6 [name]" nether: not-allowed: "&c You are not allowed to set your home in the Nether." confirmation: "&c Are you sure you want to set your home in the Nether?" @@ -534,6 +540,11 @@ commands: name-already-exists: "&c There is already an island with that name in this gamemode." parameters: "" success: "&a Successfully set your island's name to &b [name]&a ." + renamehome: + description: "rename a home location" + parameters: "[home name]" + enter-new-name: "&6 Enter the new name" + already-exists: "&c That name already exists, try another name." resetname: description: "reset your island name" success: "&a Successfully reset your island name." diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelpTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelpTest.java deleted file mode 100644 index 329e113e9..000000000 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/CustomIslandMultiHomeHelpTest.java +++ /dev/null @@ -1,203 +0,0 @@ -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.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import java.util.HashMap; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitScheduler; -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.Settings; -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.IslandsManager; -import world.bentobox.bentobox.managers.PlayersManager; - -/** - * @author tastybento - * - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({Bukkit.class, BentoBox.class}) -public class CustomIslandMultiHomeHelpTest { - - @Mock - private User user; - private CustomIslandMultiHomeHelp ch; - @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); - - // Settings - Settings s = mock(Settings.class); - when(plugin.getSettings()).thenReturn(s); - - // Player - Player player = mock(Player.class); - when(user.isOp()).thenReturn(false); - when(user.isPlayer()).thenReturn(true); - UUID uuid = UUID.randomUUID(); - when(user.getUniqueId()).thenReturn(uuid); - when(user.getPlayer()).thenReturn(player); - when(user.hasPermission(anyString())).thenReturn(true); - when(user.getTranslation(any())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); - User.setPlugin(plugin); - // Set up user already - User.getInstance(player); - - // Parent command has no aliases - CompositeCommand ic = mock(CompositeCommand.class); - when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); - when(ic.getParameters()).thenReturn("parameters"); - when(ic.getDescription()).thenReturn("description"); - when(ic.getPermission()).thenReturn("permission"); - when(ic.getUsage()).thenReturn(""); - - - // No island for player to begin with (set it later in the tests) - IslandsManager im = mock(IslandsManager.class); - when(im.hasIsland(any(), Mockito.eq(uuid))).thenReturn(false); - when(im.isOwner(any(), Mockito.eq(uuid))).thenReturn(false); - // Has team - when(im.inTeam(any(), Mockito.eq(uuid))).thenReturn(true); - when(plugin.getIslands()).thenReturn(im); - - - PlayersManager pm = mock(PlayersManager.class); - when(plugin.getPlayers()).thenReturn(pm); - - // Server & Scheduler - BukkitScheduler sch = mock(BukkitScheduler.class); - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getScheduler()).thenReturn(sch); - - // IWM friendly name - when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); - when(plugin.getIWM()).thenReturn(iwm); - - // Locales - - - // Command - ch = new CustomIslandMultiHomeHelp(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.CustomIslandMultiHomeHelp#CustomIslandMultiHomeHelp(world.bentobox.bentobox.api.commands.CompositeCommand)}. - */ - @Test - public void testCustomIslandMultiHomeHelp() { - assertEquals("help", ch.getLabel()); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.CustomIslandMultiHomeHelp#setup()}. - */ - @Test - public void testSetup() { - assertTrue(ch.isOnlyPlayer()); - assertEquals("parameters", ch.getParameters()); - assertEquals("description", ch.getDescription()); - assertEquals("permission", ch.getPermission()); - } - - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.CustomIslandMultiHomeHelp#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testCanExecuteUserStringListOfStringNotPlayer() { - when(user.isPlayer()).thenReturn(false); - assertFalse(ch.canExecute(user, "", Collections.emptyList())); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.CustomIslandMultiHomeHelp#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testCanExecuteUserStringListOfStringNoPerm() { - when(user.hasPermission(Mockito.anyString())).thenReturn(false); - assertFalse(ch.canExecute(user, "", Collections.emptyList())); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.CustomIslandMultiHomeHelp#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfString() { - assertTrue(ch.execute(user, "", Collections.emptyList())); - verify(user).sendMessage( - "commands.help.syntax", - "[usage]", - "", - "[parameters]", - "", - "[description]", - "description" - ); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.CustomIslandMultiHomeHelp#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringMaxHomes() { - when(user.getPermissionValue(Mockito.anyString(), Mockito.anyInt())).thenReturn(20); - assertTrue(ch.execute(user, "", Collections.emptyList())); - verify(user).sendMessage( - "commands.help.syntax", - "[usage]", - "", - "[parameters]", - "parameters", - "[description]", - "description" - ); - } - -} diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java index fdd7851ab..ad9062fbe 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java @@ -4,7 +4,6 @@ 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; @@ -25,6 +24,7 @@ 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; @@ -35,7 +35,6 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.commands.CompositeCommand; 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; @@ -52,11 +51,16 @@ import world.bentobox.bentobox.util.Util; @PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) public class IslandSethomeCommandTest { + @Mock private CompositeCommand ic; + @Mock private User user; private UUID uuid; + @Mock private IslandsManager im; + @Mock private Island island; + @Mock private IslandWorldManager iwm; /** @@ -78,8 +82,6 @@ public class IslandSethomeCommandTest { // Player Player player = mock(Player.class); - // Sometimes use withSettings().verboseLogging() - user = mock(User.class); when(user.isOp()).thenReturn(false); uuid = UUID.randomUUID(); when(user.getUniqueId()).thenReturn(uuid); @@ -89,13 +91,11 @@ public class IslandSethomeCommandTest { when(user.getTranslation(anyString())).thenAnswer(i -> i.getArgument(0, String.class)); // Parent command has no aliases - ic = mock(CompositeCommand.class); when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ic.getTopLabel()).thenReturn("island"); when(ic.getPermissionPrefix()).thenReturn("bskyblock."); // No island for player to begin with (set it later in the tests) - im = mock(IslandsManager.class); when(im.hasIsland(any(), any(User.class))).thenReturn(false); when(im.isOwner(any(), eq(uuid))).thenReturn(false); when(plugin.getIslands()).thenReturn(im); @@ -111,13 +111,15 @@ public class IslandSethomeCommandTest { when(Bukkit.getScheduler()).thenReturn(sch); // Island Banned list initialization - island = mock(Island.class); when(island.getBanned()).thenReturn(new HashSet<>()); when(island.isBanned(any())).thenReturn(false); + when(island.getOwner()).thenReturn(uuid); + when(island.onIsland(any())).thenReturn(true); + when(im.getMaxHomes(eq(island))).thenReturn(1); when(im.getIsland(any(), any(UUID.class))).thenReturn(island); + when(im.getIsland(any(), any(User.class))).thenReturn(island); // IWM friendly name - iwm = mock(IslandWorldManager.class); when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); // Not in nether when(iwm.isNether(any())).thenReturn(false); @@ -129,8 +131,7 @@ public class IslandSethomeCommandTest { // Number of homes PowerMockito.mockStatic(Util.class); - // 1 home for now - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(1); + } @After @@ -163,8 +164,8 @@ public class IslandSethomeCommandTest { */ @Test public void testCanExecuteNoIsland() { - // Player doesn't have an island and doesn't have a team. - when(im.inTeam(any(), eq(uuid))).thenReturn(false); + // Player doesn't have an island + when(im.getIsland(any(), eq(user))).thenReturn(null); IslandSethomeCommand isc = new IslandSethomeCommand(ic); assertFalse(isc.canExecute(user, "island", Collections.emptyList())); @@ -176,8 +177,7 @@ public class IslandSethomeCommandTest { */ @Test public void testCanExecuteNotOnIsland() { - when(im.hasIsland(any(), any(User.class))).thenReturn(true); - when(im.locationIsOnIsland(any(), any())).thenReturn(false); + when(island.onIsland(any())).thenReturn(false); IslandSethomeCommand isc = new IslandSethomeCommand(ic); assertFalse(isc.canExecute(user, "island", Collections.emptyList())); verify(user, never()).sendMessage("general.errors.no-island"); @@ -189,8 +189,6 @@ public class IslandSethomeCommandTest { */ @Test public void testCanExecute() { - when(im.hasIsland(any(), any(User.class))).thenReturn(true); - when(im.locationIsOnIsland(any(), any())).thenReturn(true); IslandSethomeCommand isc = new IslandSethomeCommand(ic); assertTrue(isc.canExecute(user, "island", Collections.emptyList())); verify(user, never()).sendMessage("general.errors.no-island"); @@ -203,63 +201,33 @@ public class IslandSethomeCommandTest { @Test public void testExecuteUserStringListOfString() { IslandSethomeCommand isc = new IslandSethomeCommand(ic); + assertTrue(isc.canExecute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList())); verify(user).sendMessage("commands.island.sethome.home-set"); } - /** * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUserStringListOfStringNoMultiHome() { + public void testExecuteUserStringListOfStringHomeSuccess() { + when(island.getMaxHomes()).thenReturn(5); IslandSethomeCommand isc = new IslandSethomeCommand(ic); - assertFalse(isc.execute(user, "island", Collections.singletonList("3"))); - verify(user).sendMessage("general.errors.no-permission", TextVariables.PERMISSION, "bskyblock.island.maxhomes.[number]"); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringMultiHomeSuccess() { - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(5); - IslandSethomeCommand isc = new IslandSethomeCommand(ic); - assertTrue(isc.execute(user, "island", Collections.singletonList("3"))); + assertTrue(isc.canExecute(user, "island", Collections.singletonList("home"))); + assertTrue(isc.execute(user, "island", Collections.singletonList("home"))); verify(user).sendMessage("commands.island.sethome.home-set"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test - public void testExecuteUserStringListOfStringMultiHomeTooHigh() { - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(5); + public void testExecuteUserStringListOfStringMultiHomeTooMany() { + when(island.getMaxHomes()).thenReturn(3); + when(im.getNumberOfHomesIfAdded(eq(island), anyString())).thenReturn(4); IslandSethomeCommand isc = new IslandSethomeCommand(ic); - assertFalse(isc.execute(user, "island", Collections.singletonList("13"))); - verify(user).sendMessage(eq("commands.island.sethome.num-homes"), eq("[number]"), eq("5")); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringMultiHomeTooLow() { - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(5); - IslandSethomeCommand isc = new IslandSethomeCommand(ic); - assertFalse(isc.execute(user, "island", Collections.singletonList("-3"))); - verify(user).sendMessage(eq("commands.island.sethome.num-homes"), eq("[number]"), eq("5")); - } - - /** - * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSethomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ - @Test - public void testExecuteUserStringListOfStringMultiHomeNAN() { - when(user.getPermissionValue(anyString(), anyInt())).thenReturn(5); - IslandSethomeCommand isc = new IslandSethomeCommand(ic); - assertFalse(isc.execute(user, "island", Collections.singletonList("six"))); - verify(user).sendMessage(eq("commands.island.sethome.num-homes"), eq("[number]"), eq("5")); + assertFalse(isc.canExecute(user, "island", Collections.singletonList("13"))); + verify(user).sendMessage(eq("commands.island.sethome.too-many-homes"), eq("[number]"), eq("3")); } /** @@ -273,6 +241,7 @@ public class IslandSethomeCommandTest { when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false); when(iwm.getWorldSettings(any())).thenReturn(ws); IslandSethomeCommand isc = new IslandSethomeCommand(ic); + assertTrue(isc.canExecute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList())); verify(user).sendMessage("commands.island.sethome.home-set"); } @@ -319,6 +288,7 @@ public class IslandSethomeCommandTest { when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false); when(iwm.getWorldSettings(any())).thenReturn(ws); IslandSethomeCommand isc = new IslandSethomeCommand(ic); + assertTrue(isc.canExecute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList())); verify(user).sendMessage("commands.island.sethome.home-set"); } 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 ce30e3409..611de3413 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 @@ -110,7 +110,7 @@ public class IslandRespawnListenerTest { when(plugin.getIslands()).thenReturn(im); safeLocation = mock(Location.class); when(safeLocation.getWorld()).thenReturn(world); - when(im.getSafeHomeLocation(any(), any(), Mockito.anyInt())).thenReturn(safeLocation); + when(im.getSafeHomeLocation(any(), any(), Mockito.anyString())).thenReturn(safeLocation); // Sometimes use Mockito.withSettings().verboseLogging() User.setPlugin(plugin); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 76f5875d7..469eb3f8c 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -31,7 +31,6 @@ import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Chunk; -import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -190,8 +189,9 @@ public class IslandsManagerTest { when(plugin.getPlaceholdersManager()).thenReturn(placeholdersManager); when(placeholdersManager.replacePlaceholders(any(), any())).thenReturn("mock translation"); - // Has team + // Player's manager when(plugin.getPlayers()).thenReturn(pm); + when(pm.getHomeLocations(any(), any())).thenReturn(Collections.emptyMap()); // Scheduler BukkitScheduler sch = mock(BukkitScheduler.class); @@ -241,6 +241,7 @@ public class IslandsManagerTest { // Mock island cache when(islandCache.getIslandAt(any(Location.class))).thenReturn(island); + when(islandCache.get(any(), any())).thenReturn(island); optionalIsland = Optional.ofNullable(island); // User location @@ -691,9 +692,12 @@ public class IslandsManagerTest { */ @Test public void testGetSafeHomeLocation() { - when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null); - when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location); - assertEquals(location, im.getSafeHomeLocation(world, user, 0)); + im.setIslandCache(islandCache); + when(island.getHome(any())).thenReturn(location); + when(iwm.inWorld(eq(world))).thenReturn(true); + + assertEquals(location, im.getSafeHomeLocation(world, user, "")); + // Change location so that it is not safe // TODO } @@ -705,7 +709,7 @@ public class IslandsManagerTest { @Test public void testGetSafeHomeLocationWorldNotIslandWorld() { when(iwm.inWorld(world)).thenReturn(false); - assertNull(im.getSafeHomeLocation(world, user, 0)); + assertNull(im.getSafeHomeLocation(world, user, "")); } /** @@ -714,7 +718,7 @@ public class IslandsManagerTest { @Test public void testGetSafeHomeLocationNoIsland() { when(pm.getHomeLocation(eq(world), eq(user), eq(0))).thenReturn(null); - assertNull(im.getSafeHomeLocation(world, user, 0)); + assertNull(im.getSafeHomeLocation(world, user, "")); verify(plugin).logWarning(eq("null player has no island in world world!")); } @@ -734,20 +738,6 @@ public class IslandsManagerTest { assertEquals(location,im.getSpawnPoint(world)); } - /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#homeTeleport(World, Player, int)}. - */ - @SuppressWarnings("deprecation") - @Test - public void testHomeTeleportPlayerInt() { - when(iwm.getDefaultGameMode(world)).thenReturn(GameMode.SURVIVAL); - when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null); - when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location); - im.homeTeleport(world, player, 0); - verify(player).teleport(eq(location), any()); - - } - /** * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isAtSpawn(org.bukkit.Location)}. */ @@ -1316,7 +1306,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersNoOwner() { @@ -1328,7 +1318,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOfflineOwner() { @@ -1346,7 +1336,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOnlineOwnerNoPerms() { @@ -1364,7 +1354,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOnlineOwnerNoPermsCoopTrust() { @@ -1386,7 +1376,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOnlineOwnerNoPermsPreset() { @@ -1403,7 +1393,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOnlineOwnerNoPermsPresetLessThanDefault() { @@ -1420,7 +1410,7 @@ public class IslandsManagerTest { } /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxMembers(Island, Integer)}. */ @Test public void testGetMaxMembersOnlineOwnerHasPerm() { @@ -1445,7 +1435,7 @@ public class IslandsManagerTest { /** - * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#setMaxMembers(Island, Integer)}. + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#setMaxMembers(Island, Integer, Integer)}. */ @Test public void testsetMaxMembers() { @@ -1454,4 +1444,116 @@ public class IslandsManagerTest { im.setMaxMembers(island, RanksManager.MEMBER_RANK, 40); verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(40)); } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxHomes(Island)}. + */ + @Test + public void testGetMaxHomesOnlineOwnerHasPerm() { + Island island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(island.getWorld()).thenReturn(world); + when(island.getMaxHomes()).thenReturn(null); + when(iwm.getMaxHomes(eq(world))).thenReturn(4); + // Permission + when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); + PermissionAttachmentInfo pai = mock(PermissionAttachmentInfo.class); + when(pai.getValue()).thenReturn(true); + when(pai.getPermission()).thenReturn("bskyblock.island.maxhomes.8"); + Set set = Collections.singleton(pai); + when(player.getEffectivePermissions()).thenReturn(set); + // Online owner + when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); + // Test + IslandsManager im = new IslandsManager(plugin); + assertEquals(8, im.getMaxHomes(island)); + verify(island).setMaxHomes(eq(8)); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxHomes(Island)}. + */ + @Test + public void testGetMaxHomesOnlineOwnerHasNoPerm() { + Island island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(island.getWorld()).thenReturn(world); + when(island.getMaxHomes()).thenReturn(null); + when(iwm.getMaxHomes(eq(world))).thenReturn(4); + // Permission + when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); + PermissionAttachmentInfo pai = mock(PermissionAttachmentInfo.class); + when(pai.getValue()).thenReturn(true); + when(pai.getPermission()).thenReturn("bskyblock.island.something.else"); + Set set = Collections.singleton(pai); + when(player.getEffectivePermissions()).thenReturn(set); + // Online owner + when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); + // Test + IslandsManager im = new IslandsManager(plugin); + assertEquals(4, im.getMaxHomes(island)); + verify(island).setMaxHomes(eq(null)); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxHomes(Island)}. + */ + @Test + public void testGetMaxHomesIslandSetOnlineOwner() { + Island island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(island.getWorld()).thenReturn(world); + when(island.getMaxHomes()).thenReturn(20); + when(iwm.getMaxHomes(eq(world))).thenReturn(4); + // Permission + when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); + PermissionAttachmentInfo pai = mock(PermissionAttachmentInfo.class); + when(pai.getValue()).thenReturn(true); + when(pai.getPermission()).thenReturn("bskyblock.island.something.else"); + Set set = Collections.singleton(pai); + when(player.getEffectivePermissions()).thenReturn(set); + // Online owner + when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); + // Test + IslandsManager im = new IslandsManager(plugin); + assertEquals(20, im.getMaxHomes(island)); + verify(island).setMaxHomes(eq(20)); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#getMaxHomes(Island)}. + */ + @Test + public void testGetMaxHomesIslandSetOnlineOwnerLowerPerm() { + Island island = mock(Island.class); + when(island.getOwner()).thenReturn(uuid); + when(island.getWorld()).thenReturn(world); + when(island.getMaxHomes()).thenReturn(20); + when(iwm.getMaxHomes(eq(world))).thenReturn(4); + // Permission + when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); + PermissionAttachmentInfo pai = mock(PermissionAttachmentInfo.class); + when(pai.getValue()).thenReturn(true); + when(pai.getPermission()).thenReturn("bskyblock.island.maxhomes.8"); + Set set = Collections.singleton(pai); + when(player.getEffectivePermissions()).thenReturn(set); + // Online owner + when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player); + // Test + IslandsManager im = new IslandsManager(plugin); + assertEquals(8, im.getMaxHomes(island)); + verify(island).setMaxHomes(eq(8)); + } + + /** + * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#setMaxHomes(Island, Integer)}. + */ + @Test + public void testsetMaxHomes() { + Island island = mock(Island.class); + // Test + IslandsManager im = new IslandsManager(plugin); + im.setMaxHomes(island, 40); + verify(island).setMaxHomes(eq(40)); + } } diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java index e30e59132..cd425159e 100644 --- a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java +++ b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java @@ -110,7 +110,7 @@ public class SafeSpotTeleportBuilderTest { // Add location sstb.location(loc); // Add home - sstb.homeNumber(10); + sstb.homeName("my name"); // Build - expect success SafeSpotTeleport result = sstb.build(); assertEquals(sst, result); diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java deleted file mode 100644 index 5ffeaf593..000000000 --- a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java +++ /dev/null @@ -1,153 +0,0 @@ -package world.bentobox.bentobox.util.teleport; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Optional; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.plugin.PluginManager; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -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.user.User; -import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.managers.IslandWorldManager; -import world.bentobox.bentobox.managers.IslandsManager; -import world.bentobox.bentobox.managers.LocalesManager; - -/** - * @author tastybento - * - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest( { BentoBox.class, Bukkit.class }) -public class SafeSpotTeleportTest { - - @Mock - private BentoBox plugin; - @Mock - private World world; - @Mock - private BukkitScheduler sch; - @Mock - private IslandsManager im; - @Mock - private Player player; - @Mock - private Location loc; - - - @Before - public void setUp() throws Exception { - // Bukkit and scheduler - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getScheduler()).thenReturn(sch); - BukkitTask task = mock(BukkitTask.class); - when(sch.runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.any(Long.class),Mockito.any(Long.class))).thenReturn(task); - - Server server = mock(Server.class); - when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); - when(server.getWorld("world")).thenReturn(world); - when(server.getVersion()).thenReturn("BSB_Mocking"); - - PluginManager pluginManager = mock(PluginManager.class); - when(Bukkit.getPluginManager()).thenReturn(pluginManager); - - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - - Whitebox.setInternalState(BentoBox.class, "instance", plugin); - - // Users - User.setPlugin(plugin); - // Locales - final - LocalesManager lm = mock(LocalesManager.class); - when(plugin.getLocalesManager()).thenReturn(lm); - when(lm.get(any(), any())).thenReturn("mock translation"); - - // Island Manager - when(plugin.getIslands()).thenReturn(im); - // Safe location - when(im.isSafeLocation(Mockito.any())).thenReturn(true); - - Island island = mock(Island.class); - when(island.getCenter()).thenReturn(mock(Location.class)); - - // Default is that there is no island around here - Optional oi = Optional.empty(); - when(im.getIslandAt(Mockito.any())).thenReturn(oi); - - // Island world manager - IslandWorldManager iwm = mock(IslandWorldManager.class); - when(iwm.getIslandProtectionRange(Mockito.any())).thenReturn(1); - when(iwm.getDefaultGameMode(Mockito.any())).thenReturn(GameMode.SURVIVAL); - when(plugin.getIWM()).thenReturn(iwm); - - // Addon - when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); - - // Player - when(player.getGameMode()).thenReturn(GameMode.SURVIVAL); - when(loc.getWorld()).thenReturn(world); - when(loc.getBlockX()).thenReturn(0); - when(loc.getBlockY()).thenReturn(120); - when(loc.getBlockZ()).thenReturn(0); - Block block = mock(Block.class); - when(loc.getBlock()).thenReturn(block); - } - - @After - public void tearDown() { - Mockito.framework().clearInlineMocks(); - } - - /** - * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.BentoBox, org.bukkit.entity.Entity, org.bukkit.Location, java.lang.String, boolean, int)}. - */ - @Ignore("Not relevant anymore") - @Test - public void testSafeSpotTeleportImmediateSafe() throws Exception { - new SafeSpotTeleport.Builder(plugin) - .entity(player) - .failureMessage("failure message") - .homeNumber(1) - .build(); - Mockito.verify(player).teleport(loc); - } - - /** - * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.BentoBox, org.bukkit.entity.Entity, org.bukkit.Location, java.lang.String, boolean, int)}. - */ - @Ignore("Different approach used") - @Test - public void testSafeSpotTeleportNotImmediatelySafe() throws Exception { - when(im.isSafeLocation(Mockito.any())).thenReturn(false); - new SafeSpotTeleport.Builder(plugin) - .entity(player) - .failureMessage("failure message") - .homeNumber(1) - .build(); - Mockito.verify(player, Mockito.never()).teleport(loc); - Mockito.verify(sch).runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.eq(0L), Mockito.eq(1L)); - } -}