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.
This commit is contained in:
tastybento 2021-03-01 10:42:08 -08:00 committed by GitHub
parent a8473c27a9
commit b6a69d0c90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 891 additions and 703 deletions

View File

@ -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;
}
}

View File

@ -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<String> args) {
return user.isPlayer() && user.hasPermission(getPermission());
}
@Override
public boolean execute(User user, String label, List<String> 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;
}
}

View File

@ -56,7 +56,6 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
// Settings related commands // Settings related commands
new IslandSettingsCommand(this); new IslandSettingsCommand(this);
new IslandSethomeCommand(this);
new IslandSetnameCommand(this); new IslandSetnameCommand(this);
new IslandResetnameCommand(this); new IslandResetnameCommand(this);
new IslandLanguageCommand(this); new IslandLanguageCommand(this);
@ -74,6 +73,11 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
// Team commands // Team commands
new IslandTeamCommand(this); new IslandTeamCommand(this);
// Home commands
new IslandSethomeCommand(this);
new IslandDeletehomeCommand(this);
new IslandRenamehomeCommand(this);
} }

View File

@ -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<String> 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<String> 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<List<String>> tabComplete(User user, String alias, List<String> 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();
}
}
}

View File

@ -1,15 +1,17 @@
package world.bentobox.bentobox.api.commands.island; package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.apache.commons.lang.math.NumberUtils;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.DelayedTeleportCommand; 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.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.util.Util;
/** /**
* @author tastybento * @author tastybento
@ -24,8 +26,8 @@ public class IslandGoCommand extends DelayedTeleportCommand {
public void setup() { public void setup() {
setPermission("island.home"); setPermission("island.home");
setOnlyPlayer(true); setOnlyPlayer(true);
setParametersHelp("commands.island.go.parameters");
setDescription("commands.island.go.description"); setDescription("commands.island.go.description");
new CustomIslandMultiHomeHelp(this);
} }
@Override @Override
@ -47,21 +49,32 @@ public class IslandGoCommand extends DelayedTeleportCommand {
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference()); user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
return false; 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; return true;
} }
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
if (!args.isEmpty() && NumberUtils.isDigits(args.get(0))) { this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), String.join(" ", args)));
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()));
return true; return true;
} }
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> 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();
}
}
} }

View File

@ -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<String> 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<String> 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<List<String>> tabComplete(User user, String alias, List<String> 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();
}
}
}

View File

@ -2,13 +2,18 @@ package world.bentobox.bentobox.api.commands.island;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
public class IslandSethomeCommand extends ConfirmableCommand { public class IslandSethomeCommand extends ConfirmableCommand {
private @Nullable Island island;
public IslandSethomeCommand(CompositeCommand islandCommand) { public IslandSethomeCommand(CompositeCommand islandCommand) {
super(islandCommand, "sethome"); super(islandCommand, "sethome");
} }
@ -18,54 +23,34 @@ public class IslandSethomeCommand extends ConfirmableCommand {
setPermission("island.sethome"); setPermission("island.sethome");
setOnlyPlayer(true); setOnlyPlayer(true);
setDescription("commands.island.sethome.description"); setDescription("commands.island.sethome.description");
new CustomIslandMultiHomeHelp(this);
} }
@Override @Override
public boolean canExecute(User user, String label, List<String> args) { public boolean canExecute(User user, String label, List<String> args) {
island = getIslands().getIsland(getWorld(), user);
// Check island // 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"); user.sendMessage("general.errors.no-island");
return false; 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"); user.sendMessage("commands.island.sethome.must-be-on-your-island");
return false; 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; return true;
} }
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
if (args.isEmpty()) { String number = String.join(" ", args);
// 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) {
// Check if the player is in the Nether // Check if the player is in the Nether
if (getIWM().isNether(user.getWorld())) { if (getIWM().isNether(user.getWorld())) {
// Check if he is (not) allowed to set his home here // Check if he is (not) allowed to set his home here
@ -99,10 +84,16 @@ public class IslandSethomeCommand extends ConfirmableCommand {
return true; 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. // 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"); 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));
}
} }
} }

View File

@ -24,6 +24,7 @@ import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.ImmutableSet.Builder;
@ -203,6 +204,19 @@ public class Island implements DataObject, MetaDataAble {
@Expose @Expose
private Map<String, MetaDataValue> metaData; private Map<String, MetaDataValue> metaData;
/**
* Island homes. Replaces player homes
* @since 1.16.0
*/
@Expose
private Map<String, Location> homes;
/**
* The maximum number of homes allowed on this island. If null, then the world default is used.
*/
@Expose
private Integer maxHomes;
/* /*
* *************************** Constructors ****************************** * *************************** Constructors ******************************
*/ */
@ -1419,12 +1433,99 @@ public class Island implements DataObject, MetaDataAble {
setChanged(); setChanged();
} }
/**
* @return the homes
* @since 1.16.0
*/
@NotNull
public Map<String, Location> 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<String, Location> 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 * @return the maxMembers
* @since 1.16.0 * @since 1.16.0
*/ */
public Map<Integer, Integer> getMaxMembers() { public Map<Integer, Integer> 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<Integer, Integer> maxMembers) { public void setMaxMembers(Map<Integer, Integer> maxMembers) {
this.maxMembers = maxMembers; this.maxMembers = maxMembers;
setChanged();
} }
/** /**
@ -1457,26 +1559,22 @@ public class Island implements DataObject, MetaDataAble {
getMaxMembers().put(rank, maxMembers); getMaxMembers().put(rank, maxMembers);
} }
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override @Override
public String toString() { public String toString() {
return "Island [changed=" + changed + ", deleted=" + deleted + ", " return "Island [changed=" + changed + ", deleted=" + deleted + ", uniqueId=" + uniqueId + ", center=" + center
+ (uniqueId != null ? "uniqueId=" + uniqueId + ", " : "") + ", location=" + location + ", range=" + range + ", protectionRange=" + protectionRange
+ (center != null ? "center=" + center + ", " : "") + ", maxEverProtectionRange=" + maxEverProtectionRange + ", world=" + world + ", gameMode=" + gameMode
+ (location != null ? "location=" + location + ", " : "") + "range=" + range + ", protectionRange=" + ", name=" + name + ", createdDate=" + createdDate + ", updatedDate=" + updatedDate + ", owner="
+ protectionRange + ", maxEverProtectionRange=" + maxEverProtectionRange + ", " + owner + ", members=" + members + ", maxMembers=" + maxMembers + ", spawn=" + spawn
+ (world != null ? "world=" + world + ", " : "") + ", purgeProtected=" + purgeProtected + ", flags=" + flags + ", history=" + history
+ (gameMode != null ? "gameMode=" + gameMode + ", " : "") + (name != null ? "name=" + name + ", " : "") + ", levelHandicap=" + levelHandicap + ", spawnPoint=" + spawnPoint + ", doNotLoad=" + doNotLoad
+ "createdDate=" + createdDate + ", updatedDate=" + updatedDate + ", " + ", cooldowns=" + cooldowns + ", commandRanks=" + commandRanks + ", reserved=" + reserved
+ (owner != null ? "owner=" + owner + ", " : "") + (members != null ? "members=" + members + ", " : "") + ", metaData=" + metaData + ", homes=" + homes + ", maxHomes=" + maxHomes + "]";
+ (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 : "") + "]";
} }
} }

View File

@ -100,6 +100,7 @@ public class JoinLeaveListener implements Listener {
plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.MEMBER_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.COOP_RANK);
plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK); plugin.getIslands().getMaxMembers(i, RanksManager.TRUSTED_RANK);
plugin.getIslands().getMaxHomes(i);
}); });
} }

View File

@ -61,7 +61,7 @@ public class IslandRespawnListener extends FlagListener {
return; // world no longer available 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) { if (respawnLocation != null) {
e.setRespawnLocation(respawnLocation); e.setRespawnLocation(respawnLocation);
} }

View File

@ -9,6 +9,7 @@ import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Queue; import java.util.Queue;
@ -391,7 +392,7 @@ public class IslandsManager {
* @return Island or null * @return Island or null
*/ */
@Nullable @Nullable
public Island getIsland(@NonNull World world, @NonNull UUID uuid){ public Island getIsland(@NonNull World world, @NonNull UUID uuid) {
return islandCache.get(world, uuid); return islandCache.get(world, uuid);
} }
@ -542,6 +543,36 @@ public class IslandsManager {
island.setMaxMembers(rank, 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. * Returns the island at the location or Optional empty if there is none.
* This includes only the protected area. Use {@link #getIslandAt(Location)} * This includes only the protected area. Use {@link #getIslandAt(Location)}
@ -558,11 +589,11 @@ public class IslandsManager {
* Get a safe home location using async chunk loading and set the home location * Get a safe home location using async chunk loading and set the home location
* @param world - world * @param world - world
* @param user - user * @param user - user
* @param number - number number * @param name - home name
* @return CompletableFuture with the location found, or null * @return CompletableFuture with the location found, or null
* @since 1.14.0 * @since 1.14.0
*/ */
public CompletableFuture<Location> getAsyncSafeHomeLocation(@NonNull World world, @NonNull User user, int number) { private CompletableFuture<Location> getAsyncSafeHomeLocation(@NonNull World world, @NonNull User user, String name) {
CompletableFuture<Location> result = new CompletableFuture<>(); CompletableFuture<Location> result = new CompletableFuture<>();
// Check if the world is a gamemode world and the player has an island // Check if the world is a gamemode world and the player has an island
Location islandLoc = getIslandLocation(world, user.getUniqueId()); Location islandLoc = getIslandLocation(world, user.getUniqueId());
@ -570,10 +601,10 @@ public class IslandsManager {
result.complete(null); result.complete(null);
return result; return result;
} }
// Try the numbered home location first // Try the home location first
Location defaultHome = plugin.getPlayers().getHomeLocation(world, user, 1); Location defaultHome = getHomeLocation(world, user);
Location numberedHome = plugin.getPlayers().getHomeLocation(world, user, number); Location namedHome = getHomeLocation(world, user, name);
Location l = numberedHome != null ? numberedHome : defaultHome; Location l = namedHome != null ? namedHome : defaultHome;
if (l != null) { if (l != null) {
Util.getChunkAtAsync(l).thenRun(() -> { Util.getChunkAtAsync(l).thenRun(() -> {
// Check if it is safe // Check if it is safe
@ -585,25 +616,25 @@ public class IslandsManager {
Location lPlusOne = l.clone().add(new Vector(0, 1, 0)); Location lPlusOne = l.clone().add(new Vector(0, 1, 0));
if (isSafeLocation(lPlusOne)) { if (isSafeLocation(lPlusOne)) {
// Adjust the home location accordingly // Adjust the home location accordingly
plugin.getPlayers().setHomeLocation(user, lPlusOne, number); setHomeLocation(user, lPlusOne, name);
result.complete(lPlusOne); result.complete(lPlusOne);
return; return;
} }
// Try island // Try island
tryIsland(result, islandLoc, user, number); tryIsland(result, islandLoc, user, name);
}); });
return result; return result;
} }
// Try island // Try island
tryIsland(result, islandLoc, user, number); tryIsland(result, islandLoc, user, name);
return result; return result;
} }
private void tryIsland(CompletableFuture<Location> result, Location islandLoc, @NonNull User user, int number) { private void tryIsland(CompletableFuture<Location> result, Location islandLoc, @NonNull User user, String number) {
Util.getChunkAtAsync(islandLoc).thenRun(() -> { Util.getChunkAtAsync(islandLoc).thenRun(() -> {
World w = islandLoc.getWorld(); World w = islandLoc.getWorld();
if (isSafeLocation(islandLoc)) { 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))); result.complete(islandLoc.clone().add(new Vector(0.5D,0,0.5D)));
return; return;
} else { } else {
@ -611,14 +642,14 @@ public class IslandsManager {
// Try the default location // Try the default location
Location dl = islandLoc.clone().add(new Vector(0.5D, 5D, 2.5D)); Location dl = islandLoc.clone().add(new Vector(0.5D, 5D, 2.5D));
if (isSafeLocation(dl)) { if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number); setHomeLocation(user, dl, number);
result.complete(dl); result.complete(dl);
return; return;
} }
// Try just above the bedrock // Try just above the bedrock
dl = islandLoc.clone().add(new Vector(0.5D, 5D, 0.5D)); dl = islandLoc.clone().add(new Vector(0.5D, 5D, 0.5D));
if (isSafeLocation(dl)) { if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number); setHomeLocation(user, dl, number);
result.complete(dl); result.complete(dl);
return; return;
} }
@ -626,7 +657,7 @@ public class IslandsManager {
for (int y = islandLoc.getBlockY(); y < w.getMaxHeight(); y++) { for (int y = islandLoc.getBlockY(); y < w.getMaxHeight(); y++) {
dl = new Location(w, islandLoc.getX() + 0.5D, y, islandLoc.getZ() + 0.5D); dl = new Location(w, islandLoc.getX() + 0.5D, y, islandLoc.getZ() + 0.5D);
if (isSafeLocation(dl)) { if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number); setHomeLocation(user, dl, number);
result.complete(dl); result.complete(dl);
return; return;
} }
@ -643,22 +674,20 @@ public class IslandsManager {
* *
* @param world - world to check, not null * @param world - world to check, not null
* @param user - the player, 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. * @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 // Check if the world is a gamemode world
if (!plugin.getIWM().inWorld(world)) { if (!plugin.getIWM().inWorld(world)) {
return null; return null;
} }
// Try the named home location first
// Try the numbered home location first Location l = getHomeLocation(world, user, name);
Location l = plugin.getPlayers().getHomeLocation(world, user, number);
if (l == null) { if (l == null) {
// Get the default home, which may be null too, but that's okay // Get the default home, which may be null too, but that's okay
number = 1; name = "";
l = plugin.getPlayers().getHomeLocation(world, user, number); l = getHomeLocation(world, user, name);
} }
// Check if it is safe // Check if it is safe
if (l != null) { if (l != null) {
@ -670,7 +699,7 @@ public class IslandsManager {
lPlusOne.add(new Vector(0, 1, 0)); lPlusOne.add(new Vector(0, 1, 0));
if (isSafeLocation(lPlusOne)) { if (isSafeLocation(lPlusOne)) {
// Adjust the home location accordingly // Adjust the home location accordingly
plugin.getPlayers().setHomeLocation(user, lPlusOne, number); setHomeLocation(user, lPlusOne, name);
return lPlusOne; return lPlusOne;
} }
} }
@ -679,20 +708,20 @@ public class IslandsManager {
if (inTeam(world, user.getUniqueId())) { if (inTeam(world, user.getUniqueId())) {
l = getIslandLocation(world, user.getUniqueId()); l = getIslandLocation(world, user.getUniqueId());
if (l != null && isSafeLocation(l)) { if (l != null && isSafeLocation(l)) {
plugin.getPlayers().setHomeLocation(user, l, number); setHomeLocation(user, l, name);
return l; return l;
} else { } else {
// try owner's home // 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)) { if (tlh != null && isSafeLocation(tlh)) {
plugin.getPlayers().setHomeLocation(user, tlh, number); setHomeLocation(user, tlh, name);
return tlh; return tlh;
} }
} }
} else { } else {
l = getIslandLocation(world, user.getUniqueId()); l = getIslandLocation(world, user.getUniqueId());
if (l != null && isSafeLocation(l)) { 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)); return l.clone().add(new Vector(0.5D,0,0.5D));
} }
} }
@ -704,20 +733,20 @@ public class IslandsManager {
// Try the default location // Try the default location
Location dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 2.5D, 0F, 30F); Location dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 2.5D, 0F, 30F);
if (isSafeLocation(dl)) { if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number); setHomeLocation(user, dl, name);
return dl; return dl;
} }
// Try just above the bedrock // Try just above the bedrock
dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 0.5D, 0F, 30F); dl = new Location(l.getWorld(), l.getX() + 0.5D, l.getY() + 5D, l.getZ() + 0.5D, 0F, 30F);
if (isSafeLocation(dl)) { if (isSafeLocation(dl)) {
plugin.getPlayers().setHomeLocation(user, dl, number); setHomeLocation(user, dl, name);
return dl; return dl;
} }
// Try all the way up to the sky // Try all the way up to the sky
for (int y = l.getBlockY(); y < 255; y++) { for (int y = l.getBlockY(); y < 255; y++) {
final Location n = new Location(l.getWorld(), l.getX() + 0.5D, y, l.getZ() + 0.5D); final Location n = new Location(l.getWorld(), l.getX() + 0.5D, y, l.getZ() + 0.5D);
if (isSafeLocation(n)) { if (isSafeLocation(n)) {
plugin.getPlayers().setHomeLocation(user, n, number); setHomeLocation(user, n, name);
return n; return n;
} }
} }
@ -725,6 +754,210 @@ public class IslandsManager {
return null; 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<Location, Integer> 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<Location, Integer> 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<String, Location> 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 * Gets the island that is defined as spawn in this world
* @param world world * @param world world
@ -775,47 +1008,6 @@ public class IslandsManager {
return islandCache.hasIsland(world, uuid); 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 * This teleports player to their island. If not safe place can be found
* then the player is sent to spawn via /spawn command * then the player is sent to spawn via /spawn command
@ -826,7 +1018,7 @@ public class IslandsManager {
* @since 1.14.0 * @since 1.14.0
*/ */
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player) { public CompletableFuture<Boolean> 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 * @param number - a number - home location to do to
* @return CompletableFuture true if successful, false if not * @return CompletableFuture true if successful, false if not
* @since 1.14.0 * @since 1.14.0
* @deprecated Use {@link #homeTeleportAsync(World, Player, String)}
*/ */
@Deprecated
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, int number) { public CompletableFuture<Boolean> 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<Boolean> 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 * then the player is sent to spawn via /spawn command
* *
* @param world - world to check * @param world - world to check
@ -854,11 +1062,11 @@ public class IslandsManager {
* @since 1.14.0 * @since 1.14.0
*/ */
public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, boolean newIsland) { public CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, boolean newIsland) {
return homeTeleportAsync(world, player, 1, newIsland); return homeTeleportAsync(world, player, "", newIsland);
} }
private CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, int number, boolean newIsland) { private CompletableFuture<Boolean> homeTeleportAsync(@NonNull World world, @NonNull Player player, String name, boolean newIsland) {
CompletableFuture<Boolean> result = new CompletableFuture<>(); CompletableFuture<Boolean> result = new CompletableFuture<>();
User user = User.getInstance(player); User user = User.getInstance(player);
user.sendMessage("commands.island.go.teleport"); user.sendMessage("commands.island.go.teleport");
@ -875,26 +1083,27 @@ public class IslandsManager {
player.updateInventory(); player.updateInventory();
} }
} }
this.getAsyncSafeHomeLocation(world, user, number).thenAccept(home -> { this.getAsyncSafeHomeLocation(world, user, name).thenAccept(home -> {
Island island = getIsland(world, user);
if (home == null) { if (home == null) {
// Try to fix this teleport location and teleport the player if possible // Try to fix this teleport location and teleport the player if possible
new SafeSpotTeleport.Builder(plugin) new SafeSpotTeleport.Builder(plugin)
.entity(player) .entity(player)
.island(plugin.getIslands().getIsland(world, user)) .island(island)
.homeNumber(number) .homeName(name)
.thenRun(() -> teleported(world, user, number, newIsland)) .thenRun(() -> teleported(world, user, name, newIsland))
.buildFuture() .buildFuture()
.thenAccept(result::complete); .thenAccept(result::complete);
return; return;
} }
// Add home // Add home
if (plugin.getPlayers().getHomeLocations(world, player.getUniqueId()).isEmpty()) { if (getHomeLocations(island).isEmpty()) {
plugin.getPlayers().setHomeLocation(player.getUniqueId(), home); setHomeLocation(player.getUniqueId(), home);
} }
PaperLib.teleportAsync(player, home).thenAccept(b -> { PaperLib.teleportAsync(player, home).thenAccept(b -> {
// Only run the commands if the player is successfully teleported // Only run the commands if the player is successfully teleported
if (Boolean.TRUE.equals(b)) { if (Boolean.TRUE.equals(b)) {
teleported(world, user, number, newIsland); teleported(world, user, name, newIsland);
result.complete(true); result.complete(true);
} else { } else {
result.complete(false); result.complete(false);
@ -905,56 +1114,9 @@ public class IslandsManager {
return result; return result;
} }
private void teleported(World world, User user, String name, boolean newIsland) {
/** if (!name.isEmpty()) {
* Teleport player to a home location. If one cannot be found a search is done to user.sendMessage("commands.island.go.teleported", TextVariables.NUMBER, name);
* 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));
} }
// If this is a new island, then run commands and do resets // If this is a new island, then run commands and do resets
if (newIsland) { if (newIsland) {

View File

@ -51,6 +51,7 @@ public class SafeSpotTeleport {
private List<Pair<Integer, Integer>> chunksToScan; private List<Pair<Integer, Integer>> chunksToScan;
private final Runnable runnable; private final Runnable runnable;
private final CompletableFuture<Boolean> result; private final CompletableFuture<Boolean> result;
private final String homeName;
/** /**
* Teleports and entity to a safe spot on island * Teleports and entity to a safe spot on island
@ -62,6 +63,7 @@ public class SafeSpotTeleport {
this.location = builder.getLocation(); this.location = builder.getLocation();
this.portal = builder.isPortal(); this.portal = builder.isPortal();
this.homeNumber = builder.getHomeNumber(); this.homeNumber = builder.getHomeNumber();
this.homeName = builder.getHomeName();
this.runnable = builder.getRunnable(); this.runnable = builder.getRunnable();
this.result = builder.getResult(); this.result = builder.getResult();
@ -241,9 +243,9 @@ public class SafeSpotTeleport {
task.cancel(); task.cancel();
// Return to main thread and teleport the player // Return to main thread and teleport the player
Bukkit.getScheduler().runTask(plugin, () -> { 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 // 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(() -> { Util.teleportAsync(entity, loc).thenRun(() -> {
if (runnable != null) Bukkit.getScheduler().runTask(plugin, runnable); if (runnable != null) Bukkit.getScheduler().runTask(plugin, runnable);
@ -293,6 +295,7 @@ public class SafeSpotTeleport {
private final BentoBox plugin; private final BentoBox plugin;
private Entity entity; private Entity entity;
private int homeNumber = 0; private int homeNumber = 0;
private String homeName;
private boolean portal = false; private boolean portal = false;
private String failureMessage = ""; private String failureMessage = "";
private Location location; private Location location;
@ -327,12 +330,25 @@ public class SafeSpotTeleport {
* Set the home number to this number * Set the home number to this number
* @param homeNumber home number * @param homeNumber home number
* @return Builder * @return Builder
* @deprecated use {@link #homeName}
*/ */
@Deprecated
public Builder homeNumber(int homeNumber) { public Builder homeNumber(int homeNumber) {
this.homeNumber = homeNumber; this.homeNumber = homeNumber;
return this; 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 * This is a portal teleportation
* @return Builder * @return Builder
@ -428,6 +444,13 @@ public class SafeSpotTeleport {
return homeNumber; return homeNumber;
} }
/**
* @return the homeName
*/
public String getHomeName() {
return homeName;
}
/** /**
* @return the portal * @return the portal
*/ */
@ -463,5 +486,7 @@ public class SafeSpotTeleport {
public CompletableFuture<Boolean> getResult() { public CompletableFuture<Boolean> getResult() {
return result; return result;
} }
} }
} }

View File

@ -466,10 +466,11 @@ commands:
about: about:
description: "display licence details" description: "display licence details"
go: go:
parameters: "[home number]" parameters: "[home name]"
description: "teleport you to your island" description: "teleport you to your island"
teleport: "&a Teleporting 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: help:
description: "the main island command" description: "the main island command"
spawn: spawn:
@ -492,6 +493,9 @@ commands:
unknown-blueprint: "&c That blueprint has not been loaded yet." 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." 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." 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: info:
description: "display info about your island or the player's island" description: "display info about your island or the player's island"
parameters: "<player>" parameters: "<player>"
@ -518,8 +522,10 @@ commands:
sethome: sethome:
description: "set your home teleport point" description: "set your home teleport point"
must-be-on-your-island: "&c You must be on your island to set home!" 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." 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: nether:
not-allowed: "&c You are not allowed to set your home in the 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?" 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." name-already-exists: "&c There is already an island with that name in this gamemode."
parameters: "<name>" parameters: "<name>"
success: "&a Successfully set your island's name to &b [name]&a ." 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: resetname:
description: "reset your island name" description: "reset your island name"
success: "&a Successfully reset your island name." success: "&a Successfully reset your island name."

View File

@ -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<String>) 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"
);
}
}

View File

@ -4,7 +4,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -25,6 +24,7 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito; import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest; 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.Settings;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.configuration.WorldSettings; 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.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.CommandsManager;
@ -52,11 +51,16 @@ import world.bentobox.bentobox.util.Util;
@PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) @PrepareForTest({Bukkit.class, BentoBox.class, Util.class})
public class IslandSethomeCommandTest { public class IslandSethomeCommandTest {
@Mock
private CompositeCommand ic; private CompositeCommand ic;
@Mock
private User user; private User user;
private UUID uuid; private UUID uuid;
@Mock
private IslandsManager im; private IslandsManager im;
@Mock
private Island island; private Island island;
@Mock
private IslandWorldManager iwm; private IslandWorldManager iwm;
/** /**
@ -78,8 +82,6 @@ public class IslandSethomeCommandTest {
// Player // Player
Player player = mock(Player.class); Player player = mock(Player.class);
// Sometimes use withSettings().verboseLogging()
user = mock(User.class);
when(user.isOp()).thenReturn(false); when(user.isOp()).thenReturn(false);
uuid = UUID.randomUUID(); uuid = UUID.randomUUID();
when(user.getUniqueId()).thenReturn(uuid); when(user.getUniqueId()).thenReturn(uuid);
@ -89,13 +91,11 @@ public class IslandSethomeCommandTest {
when(user.getTranslation(anyString())).thenAnswer(i -> i.getArgument(0, String.class)); when(user.getTranslation(anyString())).thenAnswer(i -> i.getArgument(0, String.class));
// Parent command has no aliases // Parent command has no aliases
ic = mock(CompositeCommand.class);
when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ic.getSubCommandAliases()).thenReturn(new HashMap<>());
when(ic.getTopLabel()).thenReturn("island"); when(ic.getTopLabel()).thenReturn("island");
when(ic.getPermissionPrefix()).thenReturn("bskyblock."); when(ic.getPermissionPrefix()).thenReturn("bskyblock.");
// No island for player to begin with (set it later in the tests) // 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.hasIsland(any(), any(User.class))).thenReturn(false);
when(im.isOwner(any(), eq(uuid))).thenReturn(false); when(im.isOwner(any(), eq(uuid))).thenReturn(false);
when(plugin.getIslands()).thenReturn(im); when(plugin.getIslands()).thenReturn(im);
@ -111,13 +111,15 @@ public class IslandSethomeCommandTest {
when(Bukkit.getScheduler()).thenReturn(sch); when(Bukkit.getScheduler()).thenReturn(sch);
// Island Banned list initialization // Island Banned list initialization
island = mock(Island.class);
when(island.getBanned()).thenReturn(new HashSet<>()); when(island.getBanned()).thenReturn(new HashSet<>());
when(island.isBanned(any())).thenReturn(false); 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(UUID.class))).thenReturn(island);
when(im.getIsland(any(), any(User.class))).thenReturn(island);
// IWM friendly name // IWM friendly name
iwm = mock(IslandWorldManager.class);
when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock");
// Not in nether // Not in nether
when(iwm.isNether(any())).thenReturn(false); when(iwm.isNether(any())).thenReturn(false);
@ -129,8 +131,7 @@ public class IslandSethomeCommandTest {
// Number of homes // Number of homes
PowerMockito.mockStatic(Util.class); PowerMockito.mockStatic(Util.class);
// 1 home for now
when(user.getPermissionValue(anyString(), anyInt())).thenReturn(1);
} }
@After @After
@ -163,8 +164,8 @@ public class IslandSethomeCommandTest {
*/ */
@Test @Test
public void testCanExecuteNoIsland() { public void testCanExecuteNoIsland() {
// Player doesn't have an island and doesn't have a team. // Player doesn't have an island
when(im.inTeam(any(), eq(uuid))).thenReturn(false); when(im.getIsland(any(), eq(user))).thenReturn(null);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertFalse(isc.canExecute(user, "island", Collections.emptyList())); assertFalse(isc.canExecute(user, "island", Collections.emptyList()));
@ -176,8 +177,7 @@ public class IslandSethomeCommandTest {
*/ */
@Test @Test
public void testCanExecuteNotOnIsland() { public void testCanExecuteNotOnIsland() {
when(im.hasIsland(any(), any(User.class))).thenReturn(true); when(island.onIsland(any())).thenReturn(false);
when(im.locationIsOnIsland(any(), any())).thenReturn(false);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertFalse(isc.canExecute(user, "island", Collections.emptyList())); assertFalse(isc.canExecute(user, "island", Collections.emptyList()));
verify(user, never()).sendMessage("general.errors.no-island"); verify(user, never()).sendMessage("general.errors.no-island");
@ -189,8 +189,6 @@ public class IslandSethomeCommandTest {
*/ */
@Test @Test
public void testCanExecute() { public void testCanExecute() {
when(im.hasIsland(any(), any(User.class))).thenReturn(true);
when(im.locationIsOnIsland(any(), any())).thenReturn(true);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertTrue(isc.canExecute(user, "island", Collections.emptyList())); assertTrue(isc.canExecute(user, "island", Collections.emptyList()));
verify(user, never()).sendMessage("general.errors.no-island"); verify(user, never()).sendMessage("general.errors.no-island");
@ -203,63 +201,33 @@ public class IslandSethomeCommandTest {
@Test @Test
public void testExecuteUserStringListOfString() { public void testExecuteUserStringListOfString() {
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertTrue(isc.canExecute(user, "island", Collections.emptyList()));
assertTrue(isc.execute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList()));
verify(user).sendMessage("commands.island.sethome.home-set"); 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#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/ */
@Test @Test
public void testExecuteUserStringListOfStringNoMultiHome() { public void testExecuteUserStringListOfStringHomeSuccess() {
when(island.getMaxHomes()).thenReturn(5);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertFalse(isc.execute(user, "island", Collections.singletonList("3"))); assertTrue(isc.canExecute(user, "island", Collections.singletonList("home")));
verify(user).sendMessage("general.errors.no-permission", TextVariables.PERMISSION, "bskyblock.island.maxhomes.[number]"); assertTrue(isc.execute(user, "island", Collections.singletonList("home")));
}
/**
* 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")));
verify(user).sendMessage("commands.island.sethome.home-set"); 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 @Test
public void testExecuteUserStringListOfStringMultiHomeTooHigh() { public void testExecuteUserStringListOfStringMultiHomeTooMany() {
when(user.getPermissionValue(anyString(), anyInt())).thenReturn(5); when(island.getMaxHomes()).thenReturn(3);
when(im.getNumberOfHomesIfAdded(eq(island), anyString())).thenReturn(4);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertFalse(isc.execute(user, "island", Collections.singletonList("13"))); assertFalse(isc.canExecute(user, "island", Collections.singletonList("13")));
verify(user).sendMessage(eq("commands.island.sethome.num-homes"), eq("[number]"), eq("5")); verify(user).sendMessage(eq("commands.island.sethome.too-many-homes"), eq("[number]"), eq("3"));
}
/**
* 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"));
} }
/** /**
@ -273,6 +241,7 @@ public class IslandSethomeCommandTest {
when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false); when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false);
when(iwm.getWorldSettings(any())).thenReturn(ws); when(iwm.getWorldSettings(any())).thenReturn(ws);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertTrue(isc.canExecute(user, "island", Collections.emptyList()));
assertTrue(isc.execute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList()));
verify(user).sendMessage("commands.island.sethome.home-set"); verify(user).sendMessage("commands.island.sethome.home-set");
} }
@ -319,6 +288,7 @@ public class IslandSethomeCommandTest {
when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false); when(ws.isRequireConfirmationToSetHomeInNether()).thenReturn(false);
when(iwm.getWorldSettings(any())).thenReturn(ws); when(iwm.getWorldSettings(any())).thenReturn(ws);
IslandSethomeCommand isc = new IslandSethomeCommand(ic); IslandSethomeCommand isc = new IslandSethomeCommand(ic);
assertTrue(isc.canExecute(user, "island", Collections.emptyList()));
assertTrue(isc.execute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList()));
verify(user).sendMessage("commands.island.sethome.home-set"); verify(user).sendMessage("commands.island.sethome.home-set");
} }

View File

@ -110,7 +110,7 @@ public class IslandRespawnListenerTest {
when(plugin.getIslands()).thenReturn(im); when(plugin.getIslands()).thenReturn(im);
safeLocation = mock(Location.class); safeLocation = mock(Location.class);
when(safeLocation.getWorld()).thenReturn(world); 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() // Sometimes use Mockito.withSettings().verboseLogging()
User.setPlugin(plugin); User.setPlugin(plugin);

View File

@ -31,7 +31,6 @@ import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
@ -190,8 +189,9 @@ public class IslandsManagerTest {
when(plugin.getPlaceholdersManager()).thenReturn(placeholdersManager); when(plugin.getPlaceholdersManager()).thenReturn(placeholdersManager);
when(placeholdersManager.replacePlaceholders(any(), any())).thenReturn("mock translation"); when(placeholdersManager.replacePlaceholders(any(), any())).thenReturn("mock translation");
// Has team // Player's manager
when(plugin.getPlayers()).thenReturn(pm); when(plugin.getPlayers()).thenReturn(pm);
when(pm.getHomeLocations(any(), any())).thenReturn(Collections.emptyMap());
// Scheduler // Scheduler
BukkitScheduler sch = mock(BukkitScheduler.class); BukkitScheduler sch = mock(BukkitScheduler.class);
@ -241,6 +241,7 @@ public class IslandsManagerTest {
// Mock island cache // Mock island cache
when(islandCache.getIslandAt(any(Location.class))).thenReturn(island); when(islandCache.getIslandAt(any(Location.class))).thenReturn(island);
when(islandCache.get(any(), any())).thenReturn(island);
optionalIsland = Optional.ofNullable(island); optionalIsland = Optional.ofNullable(island);
// User location // User location
@ -691,9 +692,12 @@ public class IslandsManagerTest {
*/ */
@Test @Test
public void testGetSafeHomeLocation() { public void testGetSafeHomeLocation() {
when(pm.getHomeLocation(any(), any(User.class), eq(0))).thenReturn(null); im.setIslandCache(islandCache);
when(pm.getHomeLocation(any(), any(User.class), eq(1))).thenReturn(location); when(island.getHome(any())).thenReturn(location);
assertEquals(location, im.getSafeHomeLocation(world, user, 0)); when(iwm.inWorld(eq(world))).thenReturn(true);
assertEquals(location, im.getSafeHomeLocation(world, user, ""));
// Change location so that it is not safe // Change location so that it is not safe
// TODO // TODO
} }
@ -705,7 +709,7 @@ public class IslandsManagerTest {
@Test @Test
public void testGetSafeHomeLocationWorldNotIslandWorld() { public void testGetSafeHomeLocationWorldNotIslandWorld() {
when(iwm.inWorld(world)).thenReturn(false); 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 @Test
public void testGetSafeHomeLocationNoIsland() { public void testGetSafeHomeLocationNoIsland() {
when(pm.getHomeLocation(eq(world), eq(user), eq(0))).thenReturn(null); 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!")); verify(plugin).logWarning(eq("null player has no island in world world!"));
} }
@ -734,20 +738,6 @@ public class IslandsManagerTest {
assertEquals(location,im.getSpawnPoint(world)); 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)}. * 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 @Test
public void testGetMaxMembersNoOwner() { 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 @Test
public void testGetMaxMembersOfflineOwner() { 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 @Test
public void testGetMaxMembersOnlineOwnerNoPerms() { 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 @Test
public void testGetMaxMembersOnlineOwnerNoPermsCoopTrust() { 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 @Test
public void testGetMaxMembersOnlineOwnerNoPermsPreset() { 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 @Test
public void testGetMaxMembersOnlineOwnerNoPermsPresetLessThanDefault() { 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 @Test
public void testGetMaxMembersOnlineOwnerHasPerm() { 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 @Test
public void testsetMaxMembers() { public void testsetMaxMembers() {
@ -1454,4 +1444,116 @@ public class IslandsManagerTest {
im.setMaxMembers(island, RanksManager.MEMBER_RANK, 40); im.setMaxMembers(island, RanksManager.MEMBER_RANK, 40);
verify(island).setMaxMembers(eq(RanksManager.MEMBER_RANK), eq(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<PermissionAttachmentInfo> 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<PermissionAttachmentInfo> 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<PermissionAttachmentInfo> 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<PermissionAttachmentInfo> 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));
}
} }

View File

@ -110,7 +110,7 @@ public class SafeSpotTeleportBuilderTest {
// Add location // Add location
sstb.location(loc); sstb.location(loc);
// Add home // Add home
sstb.homeNumber(10); sstb.homeName("my name");
// Build - expect success // Build - expect success
SafeSpotTeleport result = sstb.build(); SafeSpotTeleport result = sstb.build();
assertEquals(sst, result); assertEquals(sst, result);

View File

@ -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<Island> 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));
}
}