diff --git a/src/main/java/world/bentobox/warps/Warp.java b/src/main/java/world/bentobox/warps/Warp.java index 1cbdd71..e48f2a1 100644 --- a/src/main/java/world/bentobox/warps/Warp.java +++ b/src/main/java/world/bentobox/warps/Warp.java @@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; import world.bentobox.level.Level; +import world.bentobox.warps.commands.ToggleWarpCommand; import world.bentobox.warps.commands.WarpCommand; import world.bentobox.warps.commands.WarpsCommand; import world.bentobox.warps.config.Settings; @@ -100,6 +101,7 @@ public class Warp extends Addon { // Load the master warp and warps command new WarpCommand(this); new WarpsCommand(this); + new ToggleWarpCommand(this); } } @@ -140,6 +142,7 @@ public class Warp extends Addon { new WarpCommand(this, gameModeAddon.getPlayerCommand().get()); new WarpsCommand(this, gameModeAddon.getPlayerCommand().get()); + new ToggleWarpCommand(this, gameModeAddon.getPlayerCommand().get()); this.hooked = true; } }); @@ -288,7 +291,7 @@ public class Warp extends Addon { } return switch (requestLabel) { case "getSortedWarps" -> getWarpSignsManager().getSortedWarps(world); - case "getWarp" -> uuid == null ? null : getWarpSignsManager().getWarp(world, uuid); + case "getWarp" -> uuid == null ? null : getWarpSignsManager().getWarpLocation(world, uuid); case "getWarpMap" -> getWarpSignsManager().getWarpMap(world); case "hasWarp" -> uuid == null ? null : getWarpSignsManager().hasWarp(world, uuid); case "listWarps" -> getWarpSignsManager().listWarps(world); diff --git a/src/main/java/world/bentobox/warps/commands/ToggleWarpCommand.java b/src/main/java/world/bentobox/warps/commands/ToggleWarpCommand.java new file mode 100644 index 0000000..4885697 --- /dev/null +++ b/src/main/java/world/bentobox/warps/commands/ToggleWarpCommand.java @@ -0,0 +1,61 @@ +package world.bentobox.warps.commands; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.warps.Warp; +import world.bentobox.warps.event.WarpToggleEvent; +import world.bentobox.warps.objects.PlayerWarp; + +import java.util.List; +import java.util.UUID; + +public class ToggleWarpCommand extends CompositeCommand { + + private final Warp addon; + + public ToggleWarpCommand(Warp addon, CompositeCommand bsbIslandCmd) { + super(bsbIslandCmd, addon.getSettings().getToggleWarpCommand()); + this.addon = addon; + } + + public ToggleWarpCommand(Warp addon) { + super(addon.getSettings().getToggleWarpCommand()); + this.addon = addon; + } + + + @Override + public void setup() { + this.setPermission(this.getParent() == null ? Warp.WELCOME_WARP_SIGNS + ".togglewarp" : "island.warp.toggle"); + this.setOnlyPlayer(true); + this.setDescription("togglewarp.help.description"); + } + + @Override + public boolean execute(User user, String s, List list) { + UUID userUUID = user.getUniqueId(); + World userWorld = user.getWorld(); + + // Check if the user has a warp + boolean hasWarp = addon.getWarpSignsManager().hasWarp(userWorld, userUUID); + + if (hasWarp) { + // If the user has a warp, toggle its visibility + PlayerWarp warp = addon.getWarpSignsManager().getPlayerWarp(userWorld, userUUID); + // Check extreme case if PlayerWarp is null + if (warp == null) { + user.sendMessage("togglewarp.error.generic"); + return false; + } + warp.toggle(); + Bukkit.getPluginManager().callEvent(new WarpToggleEvent(userUUID, warp)); + String message = warp.isEnabled() ? "togglewarp.enabled" : "togglewarp.disabled"; + user.sendMessage(message); + } else { + user.sendMessage("togglewarp.error.no-warp"); + } + return false; + } +} diff --git a/src/main/java/world/bentobox/warps/commands/WarpCommand.java b/src/main/java/world/bentobox/warps/commands/WarpCommand.java index 53af311..bb4e445 100644 --- a/src/main/java/world/bentobox/warps/commands/WarpCommand.java +++ b/src/main/java/world/bentobox/warps/commands/WarpCommand.java @@ -50,12 +50,12 @@ public class WarpCommand extends DelayedTeleportCommand { user.sendMessage("warps.warpTip", "[text]", addon.getSettings().getWelcomeLine()); return false; } else { - // Attemp to find warp with exact player's name + // Attempt to find warp with exact player's name UUID foundWarp = warpList.stream().filter(u -> getPlayers().getName(u).equalsIgnoreCase(args.get(0))).findFirst().orElse(null); if (foundWarp == null) { - // Atempt to find warp which starts with the given name + // Attempt to find warp which starts with the given name UUID foundAlernativeWarp = warpList.stream().filter(u -> getPlayers().getName(u).toLowerCase().startsWith(args.get(0).toLowerCase())).findFirst().orElse(null); if (foundAlernativeWarp == null) { diff --git a/src/main/java/world/bentobox/warps/config/Settings.java b/src/main/java/world/bentobox/warps/config/Settings.java index 883687b..b0bb4ba 100644 --- a/src/main/java/world/bentobox/warps/config/Settings.java +++ b/src/main/java/world/bentobox/warps/config/Settings.java @@ -61,6 +61,8 @@ public class Settings implements ConfigObject String warpCommand = "warp"; @ConfigEntry(path = "warps-command") String warpsCommand = "warps"; + @ConfigEntry(path = "togglewarp-command") + String toggleWarpCommand = "togglewarp"; // --------------------------------------------------------------------- // Section: Constructor @@ -205,6 +207,21 @@ public class Settings implements ConfigObject this.warpsCommand = warpsCommand; } + + /** + * @return the toggleWarpCommand + */ + public String getToggleWarpCommand() { + return toggleWarpCommand; + } + + /** + * @param toggleWarpCommand the toggleWarpCommand to set + */ + public void setToggleWarpCommand(String toggleWarpCommand) { + this.toggleWarpCommand = toggleWarpCommand; + } + /** * @return the removeExistingWarpsWhenFlagChanges */ diff --git a/src/main/java/world/bentobox/warps/event/WarpCreateEvent.java b/src/main/java/world/bentobox/warps/event/WarpCreateEvent.java index d9430b9..cd7986a 100644 --- a/src/main/java/world/bentobox/warps/event/WarpCreateEvent.java +++ b/src/main/java/world/bentobox/warps/event/WarpCreateEvent.java @@ -15,7 +15,7 @@ import world.bentobox.warps.Warp; * @author Poslovitch * */ -public class WarpCreateEvent extends Event{ +public class WarpCreateEvent extends Event { private static final HandlerList handlers = new HandlerList(); private final Location warpLoc; diff --git a/src/main/java/world/bentobox/warps/event/WarpToggleEvent.java b/src/main/java/world/bentobox/warps/event/WarpToggleEvent.java new file mode 100644 index 0000000..b4f0460 --- /dev/null +++ b/src/main/java/world/bentobox/warps/event/WarpToggleEvent.java @@ -0,0 +1,72 @@ +package world.bentobox.warps.event; + +import org.bukkit.Location; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import world.bentobox.warps.objects.PlayerWarp; + +import java.util.UUID; + +/** + * This event is fired when a warp is toggled + * A Listener to this event can use it only to get information. e.g: broadcast something + * + * @since 1.16.0 + * @author TreemanKing + */ +public class WarpToggleEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + + private final UUID user; + private final PlayerWarp playerWarp; + + public WarpToggleEvent(UUID user, PlayerWarp playerWarp) { + this.playerWarp = playerWarp; + this.user = user; + } + + /** + * Gets the user who has toggled the warp + * + * @return the UUID of the player who toggled the warp + */ + public UUID getUser() { + return user; + } + + /** + * Gets the state of the warp + * + * @return true if the warp is enabled, false otherwise + */ + public boolean isEnabled() { + return playerWarp.isEnabled(); + } + + /** + * Gets the PlayerWarp object + * + * @return the PlayerWarp object + */ + public PlayerWarp getPlayerWarp() { + return playerWarp; + } + + /** + * Gets the location of the toggled warp + * + * @return the location of the warp + */ + public Location getLocation() { + return playerWarp.getLocation(); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/world/bentobox/warps/listeners/WarpSignsListener.java b/src/main/java/world/bentobox/warps/listeners/WarpSignsListener.java index dce3236..3d981f7 100644 --- a/src/main/java/world/bentobox/warps/listeners/WarpSignsListener.java +++ b/src/main/java/world/bentobox/warps/listeners/WarpSignsListener.java @@ -28,6 +28,7 @@ import world.bentobox.bentobox.api.events.team.TeamLeaveEvent; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Util; +import world.bentobox.warps.objects.PlayerWarp; import world.bentobox.warps.Warp; import world.bentobox.warps.event.WarpRemoveEvent; @@ -60,12 +61,12 @@ public class WarpSignsListener implements Listener { @Override public void run() { boolean changed = false; - Iterator> iterator = + Iterator> iterator = addon.getWarpSignsManager().getWarpMap(event.getWorld()).entrySet().iterator(); while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); + Map.Entry entry = iterator.next(); UUID uuid = entry.getKey(); - Location location = entry.getValue(); + Location location = entry.getValue().getLocation(); if (event.getChunk().getX() == location.getBlockX() >> 4 && event.getChunk().getZ() == location.getBlockZ() >> 4 && !Tag.SIGNS.isTagged(location.getBlock().getType())) { @@ -126,16 +127,16 @@ public class WarpSignsListener implements Listener { private boolean isPlayersSign(Player player, Block b, boolean inWorld) { // Welcome sign detected - check to see if it is this player's sign - Map list = addon.getWarpSignsManager().getWarpMap(b.getWorld()); + Map list = addon.getWarpSignsManager().getWarpMap(b.getWorld()); String reqPerm = inWorld ? addon.getPermPrefix(b.getWorld()) + "mod.removesign" : Warp.WELCOME_WARP_SIGNS + ".mod.removesign"; - return ((list.containsKey(player.getUniqueId()) && list.get(player.getUniqueId()).equals(b.getLocation())) + return ((list.containsKey(player.getUniqueId()) && list.get(player.getUniqueId()).getLocation().equals(b.getLocation())) || player.isOp() || player.hasPermission(reqPerm)); } private boolean isWarpSign(Block b) { Sign s = (Sign) b.getState(); return s.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine()) - && addon.getWarpSignsManager().getWarpMap(b.getWorld()).containsValue(s.getLocation()); + && addon.getWarpSignsManager().getWarpMap(b.getWorld()).values().stream().anyMatch(playerWarp -> playerWarp.getLocation().equals(s.getLocation())); } /** @@ -158,19 +159,24 @@ public class WarpSignsListener implements Listener { if (noPerms(user, b.getWorld(), inWorld)) { return; } + // TODO: These checks are useless if the sign is placed outside a BSB world. + // This will mean level and rank requirements are nil in the case of allow-in-other-worlds: true. + // I'm not sure if there is a better way around this without adding new API checking for primary + // or last island accessed with relevant permissions. + // ignored. if (inWorld && noLevelOrIsland(user, b.getWorld())) { e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine()); return; } - if(!hasCorrectIslandRank(b, user)) { + if (inWorld && !hasCorrectIslandRank(b, user)) { e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine()); user.sendMessage("warps.error.not-correct-rank"); return; } // Check if the player already has a sign - final Location oldSignLoc = addon.getWarpSignsManager().getWarp(b.getWorld(), user.getUniqueId()); + final Location oldSignLoc = addon.getWarpSignsManager().getWarpLocation(b.getWorld(), user.getUniqueId()); if (oldSignLoc != null) { // A sign already exists. Check if it still there and if // so, @@ -216,15 +222,15 @@ public class WarpSignsListener implements Listener { final Island island = e.getIsland(); - final Map islandWarps = addon + final Map islandWarps = addon .getWarpSignsManager() .getWarpMap(island.getWorld()) .entrySet() .stream() - .filter(x -> island.inIslandSpace(x.getValue())) + .filter(x -> island.inIslandSpace(x.getValue().getLocation())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - for(Map.Entry entry : islandWarps.entrySet()) { + for(Map.Entry entry : islandWarps.entrySet()) { if(island.getRank(entry.getKey()) >= e.getSetTo()) continue; //The user has a lower rank than the new set value. diff --git a/src/main/java/world/bentobox/warps/managers/WarpSignsManager.java b/src/main/java/world/bentobox/warps/managers/WarpSignsManager.java index 2978d8d..18a7773 100644 --- a/src/main/java/world/bentobox/warps/managers/WarpSignsManager.java +++ b/src/main/java/world/bentobox/warps/managers/WarpSignsManager.java @@ -37,7 +37,9 @@ import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.util.Util; +import world.bentobox.warps.objects.PlayerWarp; import world.bentobox.warps.Warp; +import world.bentobox.warps.event.WarpCreateEvent; import world.bentobox.warps.event.WarpInitiateEvent; import world.bentobox.warps.objects.WarpsData; import world.bentobox.warps.panels.Utils; @@ -54,7 +56,7 @@ public class WarpSignsManager { private static final String WARPS = "warps"; private final BentoBox plugin; // Map of all warps stored as player, warp sign Location - private Map> worldsWarpList; + private Map> worldsWarpList; // Database handler for level data private final Database handler; @@ -67,7 +69,7 @@ public class WarpSignsManager { * @return map of warps */ @NonNull - public Map getWarpMap(@Nullable World world) { + public Map getWarpMap(@Nullable World world) { return worldsWarpList.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>()); } @@ -99,13 +101,15 @@ public class WarpSignsManager { return false; } // Check for warps placed in a location where there was a warp before - if (getWarpMap(loc.getWorld()).containsValue(loc)) { - // remove the warp at this location, then place it - this.removeWarp(loc); + for (PlayerWarp playerWarp : getWarpMap(loc.getWorld()).values()) { + if (playerWarp.getLocation().equals(loc)) { + this.removeWarp(loc); + break; + } } - getWarpMap(loc.getWorld()).put(playerUUID, loc); + getWarpMap(loc.getWorld()).put(playerUUID, new PlayerWarp(loc, true)); saveWarpList(); - Bukkit.getPluginManager().callEvent(new WarpInitiateEvent(addon, loc, playerUUID)); + Bukkit.getPluginManager().callEvent(new WarpCreateEvent(addon, loc, playerUUID)); return true; } @@ -118,7 +122,13 @@ public class WarpSignsManager { * @return Location of warp or null */ @Nullable - public Location getWarp(World world, UUID playerUUID) { + public Location getWarpLocation(World world, UUID playerUUID) { + PlayerWarp playerWarp = getWarpMap(world).get(playerUUID); + return playerWarp != null ? playerWarp.getLocation() : null; + } + + @Nullable + public PlayerWarp getPlayerWarp(World world, UUID playerUUID) { return getWarpMap(world).get(playerUUID); } @@ -129,7 +139,7 @@ public class WarpSignsManager { */ @NonNull public String getWarpOwner(Location location) { - return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().equals(location)) + return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().getLocation().equals(location)) .findFirst().map(en -> plugin.getPlayers().getName(en.getKey())).orElse(""); } @@ -139,7 +149,7 @@ public class WarpSignsManager { * @return Optional UUID of warp owner or empty if there is none */ public Optional getWarpOwnerUUID(Location location) { - return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().equals(location)) + return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().getLocation().equals(location)) .findFirst().map(Map.Entry::getKey); } @@ -159,6 +169,10 @@ public class WarpSignsManager { // Bigger value of time means a more recent login TreeMap map = new TreeMap<>(); getWarpMap(world).forEach((uuid, value) -> { + // If the warp is not enabled, skip this iteration + if (!value.isEnabled()) { + return; + } // If never played, will be zero long lastPlayed = addon.getServer().getOfflinePlayer(uuid).getLastPlayed(); // This aims to avoid the chance that players logged off at exactly the same time @@ -187,7 +201,11 @@ public class WarpSignsManager { public Set listWarps(@NonNull World world) { // Remove any null locations getWarpMap(world).values().removeIf(Objects::isNull); - return getWarpMap(world).entrySet().stream().filter(e -> Util.sameWorld(world, Objects.requireNonNull(e.getValue().getWorld()))).map(Map.Entry::getKey).collect(Collectors.toSet()); + // Remove any warps that have not been toggled on + Map enabledWarps = getWarpMap(world).entrySet().stream() + .filter(entry -> entry.getValue().isEnabled()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + return enabledWarps.keySet(); } /** @@ -200,7 +218,8 @@ public class WarpSignsManager { warpsData = handler.loadObject(WARPS); // Load into map if (warpsData != null) { - warpsData.getWarpSigns().forEach((location,uuid) -> { + warpsData.getWarpSigns().forEach((pw, uuid) -> { + Location location = pw.getLocation(); if (location != null && location.getWorld() != null) { if (location.getWorld().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4) && !location.getBlock().getType().name().contains("SIGN")) { @@ -208,7 +227,7 @@ public class WarpSignsManager { } // Add to map - getWarpMap(location.getWorld()).put(uuid, location); + getWarpMap(location.getWorld()).put(uuid, new PlayerWarp(location, true)); } }); } else { @@ -239,10 +258,10 @@ public class WarpSignsManager { */ public void removeWarp(Location loc) { popSign(loc); - Iterator> it = getWarpMap(loc.getWorld()).entrySet().iterator(); + Iterator> it = getWarpMap(loc.getWorld()).entrySet().iterator(); while (it.hasNext()) { - Entry en = it.next(); - if (en.getValue().equals(loc)) { + Entry en = it.next(); + if (en.getValue().getLocation().equals(loc)) { // Inform player Optional.ofNullable(addon.getServer().getPlayer(en.getKey())) .map(User::getInstance) @@ -262,7 +281,7 @@ public class WarpSignsManager { */ public void removeWarp(World world, UUID uuid) { if (getWarpMap(world).containsKey(uuid)) { - popSign(getWarpMap(world).get(uuid)); + popSign(getWarpMap(world).get(uuid).getLocation()); getWarpMap(world).remove(uuid); } @@ -298,7 +317,7 @@ public class WarpSignsManager { @NonNull public SignCacheItem getSignInfo(@NonNull World world, @NonNull UUID uuid) { //get the sign info - Location signLocation = getWarp(world, uuid); + Location signLocation = getWarpLocation(world, uuid); if (signLocation == null || !signLocation.getBlock().getType().name().contains("SIGN")) { return new SignCacheItem(); } @@ -350,6 +369,11 @@ public class WarpSignsManager { float yaw = Util.blockFaceToFloat(directionFacing); final Location actualWarp = new Location(inFront.getWorld(), inFront.getBlockX() + 0.5D, inFront.getBlockY(), inFront.getBlockZ() + 0.5D, yaw, 30F); + WarpInitiateEvent e = new WarpInitiateEvent(addon, actualWarp, user.getUniqueId()); + Bukkit.getPluginManager().callEvent(e); + if (e.isCancelled()) { + return; + } //BentoBox prevents people from teleporting to an island when //the user is banned from the island for example. //By checking if the teleport succeeded before sending the messages, @@ -387,7 +411,7 @@ public class WarpSignsManager { * @param owner - owner of the warp */ public void warpPlayer(@NonNull World world, @NonNull User user, @NonNull UUID owner) { - final Location warpSpot = getWarp(world, owner); + final Location warpSpot = getWarpLocation(world, owner); // Check if the warp spot is safe if (warpSpot == null) { user.sendMessage("warps.error.does-not-exist"); diff --git a/src/main/java/world/bentobox/warps/objects/PlayerWarp.java b/src/main/java/world/bentobox/warps/objects/PlayerWarp.java new file mode 100644 index 0000000..d12b582 --- /dev/null +++ b/src/main/java/world/bentobox/warps/objects/PlayerWarp.java @@ -0,0 +1,32 @@ +package world.bentobox.warps.objects; + +import com.google.gson.annotations.Expose; +import org.bukkit.Location; + +import java.io.Serializable; + +public class PlayerWarp implements Serializable { + + @Expose + private final Location location; + + @Expose + private boolean isEnabled; + + public PlayerWarp(Location location, boolean isEnabled) { + this.location = location; + this.isEnabled = isEnabled; + } + + public Location getLocation() { + return location; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void toggle() { + isEnabled = !isEnabled; + } +} diff --git a/src/main/java/world/bentobox/warps/objects/WarpsData.java b/src/main/java/world/bentobox/warps/objects/WarpsData.java index a09f22d..9d17cdf 100644 --- a/src/main/java/world/bentobox/warps/objects/WarpsData.java +++ b/src/main/java/world/bentobox/warps/objects/WarpsData.java @@ -17,9 +17,13 @@ public class WarpsData implements DataObject { @Expose private String uniqueId = "warps"; - @Expose + + @Deprecated @Expose private Map warpSigns = new HashMap<>(); + @Expose + private Map newWarpSigns = new HashMap<>(); + public WarpsData() { // Required by YAML database } @@ -34,24 +38,40 @@ public class WarpsData implements DataObject { this.uniqueId = uniqueId; } - public Map getWarpSigns() { - if (warpSigns == null) + public Map getWarpSigns() { + convertOldWarpSigns(); + if (newWarpSigns == null) return new HashMap<>(); - return warpSigns; - } - - public void setWarpSigns(Map warpSigns) { - this.warpSigns = warpSigns; + return newWarpSigns; } /** - * Puts all the data from the map into this objects ready for saving + * Method for converting old warp signs to new warp signs + */ + public void convertOldWarpSigns() { + if (warpSigns == null) { + return; + } + + for (Map.Entry entry : warpSigns.entrySet()) { + PlayerWarp playerWarp = new PlayerWarp(entry.getKey(), true); + newWarpSigns.put(playerWarp, entry.getValue()); + } + warpSigns = null; + } + + public void setWarpSigns(Map warpSigns) { + this.newWarpSigns = warpSigns; + } + + /** + * Puts all the data from the map into these objects ready for saving * @param worldsWarpList 2D map of warp locations by world vs UUID * @return this class filled with data */ - public WarpsData save(Map> worldsWarpList) { + public WarpsData save(Map> worldsWarpList) { getWarpSigns().clear(); - worldsWarpList.values().forEach(world -> world.forEach((uuid,location) -> warpSigns.put(location, uuid))); + worldsWarpList.values().forEach(world -> world.forEach((uuid,playerWarp) -> newWarpSigns.put(playerWarp, uuid))); return this; } diff --git a/src/main/java/world/bentobox/warps/panels/Utils.java b/src/main/java/world/bentobox/warps/panels/Utils.java index 2c4b8c8..b9ac27a 100644 --- a/src/main/java/world/bentobox/warps/panels/Utils.java +++ b/src/main/java/world/bentobox/warps/panels/Utils.java @@ -52,7 +52,7 @@ public class Utils List permissions = user.getEffectivePermissions().stream(). map(PermissionAttachmentInfo::getPermission). filter(permission -> permission.startsWith(permPrefix)). - collect(Collectors.toList()); + toList(); for (String permission : permissions) { diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index a75733f..b8de846 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -18,3 +18,6 @@ permissions: '[gamemode].island.addwarp': description: Player can create a welcome warp sign default: true + '[gamemode].island.togglewarp': + description: Player can toggle a warp sign + default: true diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 1975fd0..42c4d4a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -33,3 +33,4 @@ allow-in-other-worlds: false # Warp and warps commands. You can change them if they clash with other addons or plugins. warp-command: warp warps-command: warps +togglewarp-command: togglewarp diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 7198653..c48efee 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -60,6 +60,16 @@ warps: # Prefix for messages that are send from server. prefix: "&l&6 [BentoBox]: &r" +togglewarp: + help: + description: "toggle the warp sign" + enabled: "&a Your warp is now visible!" + disabled: "&c Your warp is now hidden!" + error: + no-permission: "&c You do not have permission to do that!" + generic: "&c An error occurred while toggling your warp." + no-warp: "&c You do not have a warp to toggle!" + protection: flags: PLACE_WARP: diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index bf15dcd..a42ee30 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -4,25 +4,50 @@ warp: description: te téléporte au Warp d'un autre joueur parameters: "" warps: - deactivate: "&cAncien panneau de Warp désactivé !" + deactivate: "&c Ancienne pancarte de warp désactivée !" error: - does-not-exist: "&cCe Warp n'existe plus !" - no-permission: "&cVous n'avez pas la permission pour faire cela !" - no-remove: "&cVous ne pouvez pas supprimer ce panneau !" - no-warps-yet: "&cIl n'y a encore aucun Warp sur ce serveur." - not-enough-level: "&cVotre niveau d'île n'est pas assez élevé pour faire cela - !" - not-on-island: "&cVous devez être sur votre île pour faire cela !" - not-safe: "&cCe Warp n'est pas sûr!" - your-level-is: "&cVotre île est seulement niveau [level] et doit être niveau [required]." + does-not-exist: "&c Oh ! Cette téléportation n'existe plus !" + no-permission: "&c Vous n'avez pas le droit de faire cela !" + no-remove: "&c Vous ne pouvez pas enlever cette pancarte !" + no-warps-yet: "&c Il n'y a pas encore de téléportation disponible" + not-enough-level: "&c Le niveau de votre île n'est pas assez élevé !" + not-on-island: "&c Vous devez être sur votre île pour faire cela !" + not-safe: "&c Cette téléportation n'est pas sûre !" + your-level-is: "&c Le niveau de votre île n'est que [level] et doit être supérieur + à [required]. Exécutez la commande level." + not-correct-rank: "&c Vous n'avez pas le grade adéquat pour poser une chaîne !" help: description: Ouvre le menu des Warps - next: "&6Page suivante" - player-warped: "& 2 [pseudo] s'est téléporté à votre panneau Warp !" - previous: "&6Page précédente" - random: "&4 Warp aléatoire " - sign-removed: "&cPanneau de Warp supprimé !" - success: "&aSuccès !" - title: Panneau Warp - warpTip: "&6Placez un panneau et écrivez [text] sur la première ligne." - warpToPlayersSign: "&6 Téléportation vers le panneau de [pseudo]" + player-warped: "&2 [name] s'est rendu à votre pancarte de téléportation [gamemode] + !" + sign-removed: "&c Panneau de téléportation enlevé !" + success: "&a Succès !" + warpTip: "&6 Placer une pancarte de téléportation avec [text] sur le dessus" + warpToPlayersSign: "&6 Téléportation a la pancarte de [player]" + gui: + titles: + warp-title: "&0&l Pancarte de téléportation" + buttons: + previous: + name: "&f&l Page précédente" + description: "&7 Aller à la page [numéro]." + next: + name: "&f&l Page suivante" + description: "&7 Passer à la page [numéro]." + warp: + name: "&f&l [name]" + description: "[sign_text]" + random: + name: "&f&l Téléportation aléatoire" + description: "&7 Hmm, où vais-je apparaître ?" + tips: + click-to-previous: "&e Cliquez sur &7 pour afficher la page précédente." + click-to-next: "&e Cliquez sur &7 pour afficher la page suivante." + click-to-warp: "&e Cliquez sur &7 pour te téléporter." + conversations: + prefix: "&l&6 [BentoBox]: &r" +protection: + flags: + PLACE_WARP: + name: Place téléportation + description: Autoriser le placement d'un panneau téléportation diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index adc87c9..a02f446 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -1,27 +1,60 @@ ---- +# ########################################################################################## +# This is a YML file. Be careful when editing. Check your edits in a YAML checker like # +# the one at http://yaml-online-parser.appspot.com # +# ########################################################################################## + warp: help: - description: 传送到该玩家的传送木牌处 - parameters: "" + description: 传送至对应玩家的坐标告示牌 + parameters: warps: - help: - description: 打开传送面板 - title: 传送木牌 - deactivate: "&c 旧传送牌已停用!" + deactivate: '&c检测到不活跃的坐标告示牌!' error: - does-not-exist: "&c 哦不!那个传送点已经没了!" - no-permission: "&c 你无权那样做!" - no-remove: "&c 你拿不掉那个牌子的!" - no-warps-yet: "&c 暂无可用传送点" - not-enough-level: "&c 你的岛等级不够高!" - not-on-island: "&c 你得在自己的岛屿上操作!" - not-safe: "&c 目标传送点不安全!" - your-level-is: "&c 你的岛现在 [level] 级,需要 [required] 级。 试试岛屿等级命令吧。" - next: "&6 次页" - player-warped: "&2 [name] 传送到了你的传送牌!" - previous: "&6 前页" - random: "&4 随机传送" - sign-removed: "&c 拆掉传送牌了!" - success: "&a 成了!" - warpTip: "&6 放个牌子第一行写 [text]" - warpToPlayersSign: "&6 正传送到 [player] 的牌子" + does-not-exist: '&c啊哦! 那个传送点已经不存在了!' + no-permission: '&c你没有权限做这件事!' + no-remove: '&c你不能移除该告示牌!' + no-warps-yet: '&c尚无可用传送点.' + not-enough-level: '&c你空岛的等级还不够高!' + not-on-island: '&c你必须在自己的空岛上做这件事!' + not-safe: '&c该传送点不安全!' + your-level-is: '&c你空岛的等级只有[level], 但必须高于[required]. 使用/is level查看等级.' + not-correct-rank: '&c你在团队中的地位不足以让你设立传送点!' + help: + description: 打开传送点面板 + player-warped: '&2 [name]传送到了你[gamemode]的坐标告示牌!' + sign-removed: '&c坐标告示牌已移除!' + success: '&a成功!' + warpTip: '&6放置一个顶部为[text]的坐标告示牌' + warpToPlayersSign: '&6正在传送至[player]的坐标告示牌' + + gui: + titles: + # The title of warp panel + warp-title: '&0&l坐标告示牌' + buttons: + # Button that is used in multi-page GUIs which allows to return to previous page. + previous: + name: '&f&l上一页' + description: '&7跳转到第[number]页' # Button that is used in multi-page GUIs which allows to go to next page. + next: + name: '&f&l下一页' + description: '&7跳转到第[number]页' # Button for a warp + warp: + name: '&f&l [name]' + description: '[sign_text]' # Button for a random warp + random: + name: '&f&l随机传送' + description: '&7嗯...我会出现在哪里?' + tips: + click-to-previous: '&e点击&7 查看上一页.' + click-to-next: '&e点击&7 查看下一页.' + click-to-warp: '&e点击&7 进行传送.' + conversations: + # Prefix for messages that are send from server. + prefix: '&l&6 [BentoBox]: &r' + +protection: + flags: + PLACE_WARP: + name: 放置传送点 + description: 允许放置坐标告示牌 diff --git a/src/test/java/world/bentobox/warps/WarpSignsManagerTest.java b/src/test/java/world/bentobox/warps/WarpSignsManagerTest.java index ea51cb0..a6e3eb2 100644 --- a/src/test/java/world/bentobox/warps/WarpSignsManagerTest.java +++ b/src/test/java/world/bentobox/warps/WarpSignsManagerTest.java @@ -8,7 +8,9 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -40,6 +42,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; @@ -60,9 +63,11 @@ import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlayersManager; import world.bentobox.bentobox.util.Util; import world.bentobox.warps.config.Settings; +import world.bentobox.warps.event.WarpCreateEvent; import world.bentobox.warps.event.WarpInitiateEvent; import world.bentobox.warps.managers.SignCacheManager; import world.bentobox.warps.managers.WarpSignsManager; +import world.bentobox.warps.objects.PlayerWarp; import world.bentobox.warps.objects.WarpsData; @@ -191,7 +196,7 @@ public class WarpSignsManagerTest { // Handler when(handler.objectExists("warps")).thenReturn(true); - Map warpMap = Collections.singletonMap(location, uuid); + Map warpMap = Collections.singletonMap(new PlayerWarp(location, true), uuid); when(load.getWarpSigns()).thenReturn(warpMap); when(handler.loadObject(anyString())).thenReturn(load); @@ -271,7 +276,8 @@ public class WarpSignsManagerTest { */ @Test public void testGetWarpMapNullLocation() { - Map warpMap = Collections.singletonMap(null, uuid); + PlayerWarp playerWarp = new PlayerWarp(null, true); + Map warpMap = Collections.singletonMap(playerWarp, uuid); when(load.getWarpSigns()).thenReturn(warpMap); wsm = new WarpSignsManager(addon, plugin); assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty()); @@ -349,23 +355,23 @@ public class WarpSignsManagerTest { public void testAddWarp() { Location loc = mock(Location.class); assertTrue(wsm.addWarp(uuid, loc)); - verify(pim).callEvent(any(WarpInitiateEvent.class)); + verify(pim).callEvent(any(WarpCreateEvent.class)); } /** - * Test method for {@link WarpSignsManager#getWarp(org.bukkit.World, java.util.UUID)}. + * Test method for {@link WarpSignsManager#getWarpLocation(org.bukkit.World, java.util.UUID)}. */ @Test public void testGetWarpWorldWorld() { - assertNull(wsm.getWarp(mock(World.class), uuid)); + assertNull(wsm.getWarpLocation(mock(World.class), uuid)); } /** - * Test method for {@link WarpSignsManager#getWarp(org.bukkit.World, java.util.UUID)}. + * Test method for {@link WarpSignsManager#getWarpLocation(org.bukkit.World, java.util.UUID)}. */ @Test public void testGetWarp() { - assertEquals(location, wsm.getWarp(world, uuid)); + assertEquals(location, wsm.getWarpLocation(world, uuid)); } /** @@ -441,6 +447,38 @@ public class WarpSignsManagerTest { PowerMockito.verifyStatic(Util.class); Util.teleportAsync(eq(p), any(), eq(TeleportCause.COMMAND)); verify(player).sendMessage(anyString()); + verify(pim).callEvent(any(WarpInitiateEvent.class)); + } + + /** + * Test method for {@link WarpSignsManager#warpPlayer(org.bukkit.World, world.bentobox.bentobox.api.user.User, java.util.UUID)}. + */ + @Test + public void testWarpPlayerEventCancelled() { + // Capture the event passed to callEvent + ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(WarpInitiateEvent.class); + + // Simulate the event being called and cancelled + doAnswer(invocation -> { + WarpInitiateEvent event = (WarpInitiateEvent) invocation.getArgument(0); + event.setCancelled(true); + return null; + }).when(pim).callEvent(eventCaptor.capture()); + + Player p = mock(Player.class); + when(p.getUniqueId()).thenReturn(UUID.randomUUID()); + when(p.getWorld()).thenReturn(world); + when(p.getName()).thenReturn("tastybento"); + when(p.getLocation()).thenReturn(location); + when(p.isOnline()).thenReturn(true); + when(p.canSee(any(Player.class))).thenReturn(true); + @Nullable + User u = User.getInstance(p); + PowerMockito.when(Util.teleportAsync(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(true)); + wsm.warpPlayer(world, u, uuid); + PowerMockito.verifyStatic(Util.class, never()); + Util.teleportAsync(eq(p), any(), eq(TeleportCause.COMMAND)); + verify(player, never()).sendMessage(anyString()); } /** diff --git a/src/test/java/world/bentobox/warps/listeners/WarpSignsListenerTest.java b/src/test/java/world/bentobox/warps/listeners/WarpSignsListenerTest.java index f98d1be..66c15b2 100644 --- a/src/test/java/world/bentobox/warps/listeners/WarpSignsListenerTest.java +++ b/src/test/java/world/bentobox/warps/listeners/WarpSignsListenerTest.java @@ -50,6 +50,7 @@ import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.util.Util; +import world.bentobox.warps.objects.PlayerWarp; import world.bentobox.warps.Warp; import world.bentobox.warps.managers.WarpSignsManager; import world.bentobox.warps.config.Settings; @@ -123,16 +124,16 @@ public class WarpSignsListenerTest { when(block.getState()).thenReturn(s); // warp signs manager when(addon.getWarpSignsManager()).thenReturn(wsm); - Map list = new HashMap<>(); + Map list = new HashMap<>(); Location location = mock(Location.class); when(location.getBlock()).thenReturn(block); when(s.getLocation()).thenReturn(location); when(block.getLocation()).thenReturn(location); - list.put(uuid, location); + list.put(uuid, new PlayerWarp(location, true)); // Player is in world when(wsm.getWarpMap(world)).thenReturn(list); //Player has a warp sign already here - when(wsm.getWarp(any(), any())).thenReturn(location); + when(wsm.getWarpLocation(any(), any())).thenReturn(location); // Unique spot when(wsm.addWarp(any(), any())).thenReturn(true); // Bentobox @@ -339,8 +340,8 @@ public class WarpSignsListenerTest { when(settings.getRemoveExistingWarpsWhenFlagChanges()).thenReturn(true); WarpSignsListener wsl = new WarpSignsListener(addon); - Map warps = Map.of( - player.getUniqueId(), block.getLocation() + Map warps = Map.of( + player.getUniqueId(), new PlayerWarp(block.getLocation(), true) ); when(wsm.getWarpMap(any())).thenReturn(warps); @@ -420,7 +421,7 @@ public class WarpSignsListenerTest { @Test public void testCreateNoSignAlreadyUniqueSpot() { - when(wsm.getWarp(any(), any())).thenReturn(null); + when(wsm.getWarpLocation(any(), any())).thenReturn(null); when(player.hasPermission(anyString())).thenReturn(true); WarpSignsListener wsl = new WarpSignsListener(addon); SignChangeEvent e = new SignChangeEvent(block, player, lines);