From be5ad3bc37592c284f3985ce2b45b5111092af8f Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 7 Sep 2026 10:31:45 -0700 Subject: [PATCH] Make admin setrank a full console team-rank tool Server staff asked to promote and demote team members without logging in. /admin setrank [island owner] already existed and ran from the console, but it was rough enough to be unreliable for that: - Only the rank's translated display name matched, so the rank had to be typed as it appears in the caller's locale. Now the keyword (member, sub-owner, trusted, coop, or any addon rank without the ranks. prefix), the translated name, or the numeric value all work, and the error lists the valid choices. - Owner, mod and admin ranks were accepted. Setting owner rank in the member map without moving the owner field left the island inconsistent. These are now refused and the admin is pointed at setowner. - With no island named, the target's primary island was used even when they owned it, so the command could demote an owner into a member. Now the island the target is a member of (but does not own) is used, and if they are on several team islands the centres are listed so one can be chosen. - The island can now be named by x,y,z centre as well as by owner, which is needed when an owner has concurrent islands. - Tab completion was off by one (players offered for the rank slot, ranks for the owner slot) and now completes the correct argument. - The affected player is told their rank changed, and a no-op change is reported instead of silently succeeding. Locale keys added under commands.admin.setrank in every bundled locale: cannot-set-owner, already-rank, admin-changed-rank; unknown-rank gained [rank] and [ranks] placeholders. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0146MpZW6KGnu5E4qkcR5NXV --- .../commands/admin/AdminSetrankCommand.java | 255 ++++++++++---- src/main/resources/locales/cs.yml | 13 +- src/main/resources/locales/de.yml | 11 +- src/main/resources/locales/en-US.yml | 11 +- src/main/resources/locales/es.yml | 9 +- src/main/resources/locales/fr.yml | 9 +- src/main/resources/locales/hr.yml | 9 +- src/main/resources/locales/hu.yml | 13 +- src/main/resources/locales/id.yml | 13 +- src/main/resources/locales/it.yml | 9 +- src/main/resources/locales/ja.yml | 9 +- src/main/resources/locales/ko.yml | 9 +- src/main/resources/locales/lv.yml | 9 +- src/main/resources/locales/nl.yml | 15 +- src/main/resources/locales/pl.yml | 9 +- src/main/resources/locales/pt-BR.yml | 15 +- src/main/resources/locales/pt.yml | 13 +- src/main/resources/locales/ro.yml | 9 +- src/main/resources/locales/ru.yml | 14 +- src/main/resources/locales/tr.yml | 9 +- src/main/resources/locales/uk.yml | 9 +- src/main/resources/locales/vi.yml | 9 +- src/main/resources/locales/zh-CN.yml | 9 +- src/main/resources/locales/zh-HK.yml | 9 +- src/main/resources/locales/zh-TW.yml | 13 +- .../admin/AdminSetrankCommandTest.java | 325 +++++++++++++----- 26 files changed, 581 insertions(+), 256 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommand.java index e2d5290e0..27daf355a 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommand.java @@ -1,10 +1,15 @@ package world.bentobox.bentobox.api.commands.admin; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.UUID; +import org.bukkit.Location; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -16,14 +21,30 @@ import world.bentobox.bentobox.util.Util; /** - * @author tastybento + * Sets a player's rank on an island directly, which covers both promotion and demotion. + *

+ * Unlike the player-facing {@code /island team promote} and {@code demote} commands, this does + * not step one rank at a time and does not depend on the rank of the admin running it. It is + * intentionally not restricted to in-game players so that server staff can manage teams from the + * console. The island is identified in one of three ways: + *

    + *
  • nothing - the island the target is a team member of, if there is exactly one
  • + *
  • the name of the island's owner
  • + *
  • the island's centre as {@code x,y,z}
  • + *
+ * Any rank between visitor and owner may be set, so this also grants or revokes coop and trusted + * status. Owner rank is refused - ownership is transferred with {@code setowner}, which keeps the + * island's owner field and the rank map in step. * + * @author tastybento */ public class AdminSetrankCommand extends CompositeCommand { + private static final String RANK_PREFIX = "ranks."; + private int rankValue; private @Nullable UUID targetUUID; - private @Nullable UUID ownerUUID; + private @Nullable Island island; public AdminSetrankCommand(CompositeCommand adminCommand) { super(adminCommand, "setrank"); @@ -39,113 +60,197 @@ public void setup() { @Override public boolean canExecute(User user, String label, List args) { + // Syntax: [island owner | x,y,z] if (args.size() != 2 && args.size() != 3) { - // Show help showHelp(this, user); return false; } - // Get target player + // Target targetUUID = Util.getUUID(args.getFirst()); if (targetUUID == null) { user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.getFirst()); return false; } - // Get rank - rankValue = RanksManager.getInstance().getRanks().entrySet().stream() - .filter(r -> user.getTranslation(r.getKey()).equalsIgnoreCase(args.get(1))).findFirst() - .map(Map.Entry::getValue).orElse(-999); - if (rankValue < RanksManager.BANNED_RANK) { - user.sendMessage("commands.admin.setrank.unknown-rank"); + // Rank + Optional rank = parseRank(user, args.get(1)); + if (rank.isEmpty()) { + user.sendMessage("commands.admin.setrank.unknown-rank", TextVariables.RANK, args.get(1), "[ranks]", + String.join(", ", getAllowedRankNames())); return false; } + rankValue = rank.get(); if (rankValue <= RanksManager.VISITOR_RANK) { user.sendMessage("commands.admin.setrank.not-possible"); return false; } + if (rankValue >= RanksManager.OWNER_RANK) { + user.sendMessage("commands.admin.setrank.cannot-set-owner"); + return false; + } + // Island + island = args.size() == 2 ? findTargetIsland(user) : findNamedIsland(user, args.get(2)); + if (island == null) { + return false; + } + if (targetUUID.equals(island.getOwner())) { + user.sendMessage("commands.admin.setrank.cannot-set-owner"); + return false; + } + if (island.getRank(targetUUID) == rankValue) { + user.sendMessage("commands.admin.setrank.already-rank", TextVariables.NAME, args.getFirst(), + TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rankValue))); + return false; + } + return true; + } - if (args.size() == 2) { - // We want to change the player's rank on the island he is part of. + @Override + public boolean execute(User user, String label, List args) { + Objects.requireNonNull(island); + Objects.requireNonNull(targetUUID); + User target = User.getInstance(targetUUID); + int currentRank = island.getRank(targetUUID); + String ownerName = getPlayers().getName(island.getOwner()); - // Check if the target is part of an island - if (!getIslands().hasIsland(getWorld(), targetUUID) && !getPlugin().getIslands().inTeam(getWorld(), targetUUID)) { - user.sendMessage("general.errors.player-has-no-island"); - return false; - } - } else { - // We want to change the player's rank on the island of the specified owner. + island.setRank(targetUUID, rankValue); + IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true) + .reason(IslandEvent.Reason.RANK_CHANGE).rankChange(currentRank, rankValue).build(); - ownerUUID = getPlayers().getUUID(args.get(2)); - if (ownerUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(2)); - return false; - } + user.sendMessage("commands.admin.setrank.rank-set", "[from]", + user.getTranslation(RanksManager.getInstance().getRank(currentRank)), "[to]", + user.getTranslation(RanksManager.getInstance().getRank(rankValue)), TextVariables.NAME, ownerName); + if (target.isOnline()) { + target.sendMessage("commands.admin.setrank.admin-changed-rank", TextVariables.RANK, + target.getTranslation(RanksManager.getInstance().getRank(rankValue)), TextVariables.NAME, + ownerName); + } + return true; + } - if (!getPlugin().getIslands().hasIsland(getWorld(), ownerUUID)) { - user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(2)); - return false; + /** + * Resolves a rank argument. Accepts the rank keyword ({@code member}, {@code sub-owner}, or any + * addon-registered rank's reference without the {@code ranks.} prefix), the rank's translated + * name in the caller's locale, or the numeric rank value. + * @return the rank value, if the argument names any known rank + */ + private Optional parseRank(User user, String arg) { + String wanted = arg.toLowerCase(Locale.ROOT); + for (Map.Entry en : RanksManager.getInstance().getRanks().entrySet()) { + String translated = Util.stripColor(user.getTranslation(en.getKey())).toLowerCase(Locale.ROOT); + if (wanted.equals(keyword(en.getKey())) || wanted.equals(translated) + || wanted.equals(String.valueOf(en.getValue()))) { + return Optional.of(en.getValue()); } } + return Optional.empty(); + } - return true; + private static String keyword(String reference) { + String key = reference.startsWith(RANK_PREFIX) ? reference.substring(RANK_PREFIX.length()) : reference; + return key.toLowerCase(Locale.ROOT); } - @Override - public boolean execute(User user, String label, List args) { - assert targetUUID != null; - User target = User.getInstance(targetUUID); - Island island; - if (ownerUUID != null) { - island = getIslands().getIsland(getWorld(), ownerUUID); - } else { - island = getIslands().getIsland(getWorld(), targetUUID); + /** + * @return the keywords of every rank this command can set, lowest first + */ + private static List getAllowedRankNames() { + return RanksManager.getInstance().getRanks().entrySet().stream() + .filter(en -> en.getValue() > RanksManager.VISITOR_RANK && en.getValue() < RanksManager.OWNER_RANK) + .sorted(Map.Entry.comparingByValue()).map(en -> keyword(en.getKey())).toList(); + } + + /** + * No island was named: use the one team island the target belongs to but does not own. + */ + private @Nullable Island findTargetIsland(User user) { + List memberOf = getIslands().getIslands(getWorld(), targetUUID).stream() + .filter(i -> !Objects.equals(targetUUID, i.getOwner())).toList(); + if (memberOf.isEmpty()) { + if (getIslands().hasIsland(getWorld(), targetUUID)) { + user.sendMessage("commands.admin.setrank.cannot-set-owner"); + } else { + user.sendMessage("general.errors.player-has-no-island"); + } + return null; } - if (island == null) { - user.sendMessage("general.errors.player-has-no-island"); - return false; + if (memberOf.size() > 1) { + user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island"); + memberOf.forEach(i -> user.sendMessage("commands.admin.unregister.errors.specify-island-location", + TextVariables.XYZ, Util.xyz(i.getCenter().toVector()))); + return null; } - int currentRank = island.getRank(target); - island.setRank(target, rankValue); - IslandEvent.builder() - .island(island) - .involvedPlayer(targetUUID) - .admin(true) - .reason(IslandEvent.Reason.RANK_CHANGE) - .rankChange(currentRank, rankValue) - .build(); - - String ownerName; - if (ownerUUID != null) { - ownerName = getPlayers().getName(ownerUUID); - } else { - ownerName = target.getName(); - } - user.sendMessage("commands.admin.setrank.rank-set", - "[from]", user.getTranslation(RanksManager.getInstance().getRank(currentRank)), "[to]", - user.getTranslation(RanksManager.getInstance().getRank(rankValue)), - TextVariables.NAME, ownerName); - return true; + return memberOf.getFirst(); } - @Override - public Optional> tabComplete(User user, String alias, List args) { - // Return the player names - if (args.size() == 2) { - return Optional.of(Util.getOnlinePlayerList(user)); + /** + * An island was named, either by its x,y,z centre or by the name of its owner. + */ + private @Nullable Island findNamedIsland(User user, String name) { + Optional coords = parseXYZ(name); + if (coords.isPresent()) { + Optional at = getIslands().getIslandAt(coords.get()); + if (at.isEmpty()) { + user.sendMessage("commands.admin.unregister.errors.unknown-island-location"); + return null; + } + return at.get(); + } + UUID ownerUUID = Util.getUUID(name); + if (ownerUUID == null) { + user.sendMessage("general.errors.unknown-player", TextVariables.NAME, name); + return null; } + List owned = getIslands().getIslands(getWorld(), ownerUUID).stream() + .filter(i -> ownerUUID.equals(i.getOwner())).toList(); + if (owned.isEmpty()) { + user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, name); + return null; + } + if (owned.size() > 1) { + // Concurrent islands: the owner alone is ambiguous, so ask for the centre + user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island"); + owned.forEach(i -> user.sendMessage("commands.admin.unregister.errors.specify-island-location", + TextVariables.XYZ, Util.xyz(i.getCenter().toVector()))); + return null; + } + return owned.getFirst(); + } - // Return the ranks - if (args.size() == 3) { - return Optional.of(RanksManager.getInstance().getRanks() - .entrySet().stream() - .filter(entry -> entry.getValue() > RanksManager.VISITOR_RANK) - .map(entry -> user.getTranslation(entry.getKey())).toList()); + /** + * @return a location in this command's world if the argument is in {@code x,y,z} form + */ + private Optional parseXYZ(String arg) { + String[] parts = arg.split(","); + if (parts.length != 3) { + return Optional.empty(); } + try { + return Optional.of(new Location(getWorld(), Integer.parseInt(parts[0].trim()), + Integer.parseInt(parts[1].trim()), Integer.parseInt(parts[2].trim()))); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } - // Return the player names again for the optional island owner argument - if (args.size() == 4) { + @Override + public Optional> tabComplete(User user, String alias, @NonNull List args) { + String lastArg = !args.isEmpty() ? args.getLast() : ""; + if (args.size() == 1) { return Optional.of(Util.getOnlinePlayerList(user)); } - + if (args.size() == 2) { + return Optional.of(Util.tabLimit(getAllowedRankNames(), lastArg)); + } + if (args.size() == 3) { + // Island owners, plus the centres of the islands the target already belongs to + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + UUID targetId = getPlayers().getUUID(args.getFirst()); + if (targetId != null) { + getIslands().getIslands(getWorld(), targetId).stream().map(i -> Util.xyz(i.getCenter().toVector())) + .forEach(options::add); + } + return Optional.of(Util.tabLimit(options, lastArg)); + } return Optional.empty(); } } diff --git a/src/main/resources/locales/cs.yml b/src/main/resources/locales/cs.yml index 6d1052980..84367cabb 100644 --- a/src/main/resources/locales/cs.yml +++ b/src/main/resources/locales/cs.yml @@ -383,13 +383,14 @@ commands: description: získat hodnost hráče na jejich ostrově nebo na ostrově vlastníka rank-is: 'Hodnost je [rank] na ostrově [name].' setrank: - parameters: [island owner] - description: nastavit hodnot hráče na jejich ostrově nebo na ostrově vlastníka - unknown-rank: 'Neznámá hodnost!' + parameters: [vlastník [prefix_island]u | x,y,z] + description: 'povýší nebo degraduje hráče na hodnost na jeho týmovém [prefix_island]u, nebo na [prefix_island]u určeném jménem vlastníka či středem x,y,z' + unknown-rank: 'Neznámá hodnost [rank]. Vyberte jednu z: [ranks]' not-possible: 'Hodnost musí být vyšší, než visitor.' - rank-set: >- - Hodnost nastavena z [from] na [to] na ostrově [name] - . + cannot-set-owner: 'Hodnost vlastníka nelze nastavit ani změnit. Použijte setowner k převodu vlastnictví.' + already-rank: '[name] už má hodnost [rank] na tomto [prefix_island]u.' + rank-set: 'Hodnost nastavena z [from] na [to] na ostrově [name] .' + admin-changed-rank: 'Admin nastavil tvou hodnost na [prefix_island]u hráče [name] na [rank].' setprotectionlocation: parameters: '[x y z souřadnice]' description: >- diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml index 39b76135d..7b95a9843 100644 --- a/src/main/resources/locales/de.yml +++ b/src/main/resources/locales/de.yml @@ -407,13 +407,14 @@ commands: description: Den Rang eines Spielers auf seiner Insel erhalten rank-is: 'Der Rang ist [rank] auf ihrer Insel.' setrank: - parameters: [Insels Besitzer] - description: >- - Den Rang eines Spielers auf seiner Insel oder der Insel des Besitzers - festlegen - unknown-rank: 'Unbekannter Rang!' + parameters: [[prefix_island]-Besitzer | x,y,z] + description: 'befördert oder degradiert einen Spieler auf einen Rang auf seiner Team-[prefix_island] oder auf der [prefix_island], die über den Besitzer oder das x,y,z-Zentrum angegeben wird' + unknown-rank: 'Unbekannter Rang [rank]. Wähle einen von: [ranks]' not-possible: 'Der Rang muss höher sein als Besucher.' + cannot-set-owner: 'Der Rang des Besitzers kann nicht gesetzt oder geändert werden. Nutze setowner, um das Eigentum zu übertragen.' + already-rank: '[name] hat auf dieser [prefix_island] bereits den Rang [rank].' rank-set: 'Rang von[from]auf[to]gesetzt für[name]''s Insel.' + admin-changed-rank: 'Ein Admin hat deinen Rang auf der [prefix_island] von [name] auf [rank] gesetzt.' setprotectionlocation: parameters: '[x y z Koordinaten]' description: >- diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 05fa7f8f0..27a11dc58 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -362,12 +362,15 @@ commands: of the owner rank-is: 'Rank is [rank] on [name]''s [prefix_island].' setrank: - parameters: [[prefix_island] owner] - description: set a player's rank on their [prefix_island] or the [prefix_island] - of the owner - unknown-rank: 'Unknown rank!' + parameters: [[prefix_island] owner | x,y,z] + description: >- + promote or demote a player to a rank on their team [prefix_island], or on the [prefix_island] named by its owner or x,y,z centre + unknown-rank: 'Unknown rank [rank]. Choose one of: [ranks]' not-possible: 'Rank must be higher than visitor.' + cannot-set-owner: 'You cannot set or change the owner''s rank. Use setowner to transfer ownership instead.' + already-rank: '[name] is already [rank] on this [prefix_island].' rank-set: 'Rank set from [from] to [to] on [name]''s [prefix_island].' + admin-changed-rank: 'An admin set your rank on [name]''s [prefix_island] to [rank].' setprotectionlocation: parameters: '[x y z coords]' description: set current location or [x y z] as center of [prefix_island]'s diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml index 9b3ac4eaf..0e51d5419 100644 --- a/src/main/resources/locales/es.yml +++ b/src/main/resources/locales/es.yml @@ -391,11 +391,14 @@ commands: description: conseguir el rango de un jugador en su isla rank-is: 'El Rango es [rank] en su isla.' setrank: - parameters: - description: Establecer el rango de un jugador en su isla - unknown-rank: 'Rango desconocido!' + parameters: [dueño de la [prefix_island] | x,y,z] + description: 'asciende o degrada a un jugador a un rango en la [prefix_island] de su equipo, o en la [prefix_island] indicada por su dueño o centro x,y,z' + unknown-rank: 'Rango desconocido [rank]. Elige uno de: [ranks]' not-possible: 'Rank debe ser más alto que el visitante' + cannot-set-owner: 'No puedes establecer ni cambiar el rango del dueño. Usa setowner para transferir la propiedad.' + already-rank: '[name] ya es [rank] en esta [prefix_island].' rank-set: 'Rango establecido desde [from] a [to].' + admin-changed-rank: 'Un administrador ha puesto tu rango en la [prefix_island] de [name] a [rank].' setprotectionlocation: parameters: '[coordenadas x y z]' description: >- diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml index 2548d460e..c958051d2 100644 --- a/src/main/resources/locales/fr.yml +++ b/src/main/resources/locales/fr.yml @@ -423,11 +423,14 @@ commands: description: obtenir le rang d'un joueur sur son île ou sur l'île du propriétaire rank-is: 'Le rang du joueur est [rank] sur l''île de [name].' setrank: - parameters: [propriétaire de l'île] - description: définir le rang d'un joueur sur son île ou sur celle du propriétaire - unknown-rank: 'Rang inconnu!' + parameters: [propriétaire de l’[prefix_island] | x,y,z] + description: 'promouvoir ou rétrograder un joueur à un rang sur l’[prefix_island] de son équipe, ou sur l’[prefix_island] désignée par son propriétaire ou son centre x,y,z' + unknown-rank: 'Rang inconnu [rank]. Choisissez parmi : [ranks]' not-possible: 'Le classement doit être supérieur à celui du visiteur.' + cannot-set-owner: 'Vous ne pouvez pas définir ni changer le rang du propriétaire. Utilisez setowner pour transférer la propriété.' + already-rank: '[name] est déjà [rank] sur cette [prefix_island].' rank-set: 'Rang défini de [from] à [to] sur l''île de [name].' + admin-changed-rank: 'Un administrateur a défini votre rang sur l’[prefix_island] de [name] à [rank].' setprotectionlocation: parameters: '[coordonnées x y z]' description: >- diff --git a/src/main/resources/locales/hr.yml b/src/main/resources/locales/hr.yml index e8324ecf4..dc14a8c9e 100644 --- a/src/main/resources/locales/hr.yml +++ b/src/main/resources/locales/hr.yml @@ -391,11 +391,14 @@ commands: description: dobiti rang igrača na svom otoku ili otoku vlasnika rank-is: 'Rang je [rank] na [name]otoku.' setrank: - parameters: [vlasnik otoka] - description: postaviti rang igrača na njegovom otoku ili otoku vlasnika - unknown-rank: 'Nepoznat rang!' + parameters: [vlasnik [prefix_island]a | x,y,z] + description: 'promakni ili degradiraj igrača na rang na njegovom timskom [prefix_island]u, ili na [prefix_island]u navedenom po vlasniku ili središtu x,y,z' + unknown-rank: 'Nepoznat rang [rank]. Odaberi jedan od: [ranks]' not-possible: 'Rang mora biti viši od posjetitelja.' + cannot-set-owner: 'Ne možeš postaviti ni promijeniti rang vlasnika. Koristi setowner za prijenos vlasništva.' + already-rank: '[name] je već [rank] na ovom [prefix_island]u.' rank-set: 'Rang postavljen od [from] do [to] na [name]otoku.' + admin-changed-rank: 'Admin je postavio tvoj rang na [prefix_island]u igrača [name] na [rank].' setprotectionlocation: parameters: '[x y z koordinate]' description: >- diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml index 59982970e..6a4c7512c 100644 --- a/src/main/resources/locales/hu.yml +++ b/src/main/resources/locales/hu.yml @@ -417,13 +417,14 @@ commands: description: kap egy játékos rangot a szigetén vagy a tulajdonos szigetén rank-is: 'Az Rank [rank] [name]szigetén.' setrank: - parameters: [szigettulajdonos] - description: állítsanak be egy játékos rangot a szigetükön vagy a tulajdonos szigetén - unknown-rank: 'Ismeretlen rang!' + parameters: [[prefix_island] tulajdonosa | x,y,z] + description: 'játékos előléptetése vagy lefokozása egy rangra a csapata [prefix_island]án, vagy a tulajdonos nevével vagy x,y,z középponttal megadott [prefix_island]on' + unknown-rank: 'Ismeretlen rang: [rank]. Válassz egyet: [ranks]' not-possible: 'A rangnak magasabbnak kell lennie, mint a látogatóé.' - rank-set: >- - Az rangsor beállítva -től [from] -ig -ig [to] -ig [name] - szigetén. + cannot-set-owner: 'A tulajdonos rangját nem lehet beállítani vagy megváltoztatni. Használd a setowner parancsot a tulajdonjog átadásához.' + already-rank: '[name] már [rank] ezen a [prefix_island]on.' + rank-set: 'Az rangsor beállítva -től [from] -ig -ig [to] -ig [name] szigetén.' + admin-changed-rank: 'Egy admin a rangodat [name] [prefix_island]án [rank] rangra állította.' setprotectionlocation: parameters: '[x y z koordináta]' description: >- diff --git a/src/main/resources/locales/id.yml b/src/main/resources/locales/id.yml index 618ae72ab..8f051f724 100644 --- a/src/main/resources/locales/id.yml +++ b/src/main/resources/locales/id.yml @@ -400,13 +400,14 @@ commands: description: mendapatkan peringkat pemain di pulau mereka atau pulau pemiliknya rank-is: 'Peringkat adalah [rank] di pulau [name].' setrank: - parameters: [pemilik pulau] - description: mengatur peringkat pemain di pulau mereka atau pulau pemiliknya - unknown-rank: 'Peringkat tidak diketahui!' + parameters: [pemilik [prefix_island] | x,y,z] + description: 'naikkan atau turunkan peringkat pemain di [prefix_island] timnya, atau di [prefix_island] yang disebutkan berdasarkan pemilik atau pusat x,y,z' + unknown-rank: 'Peringkat [rank] tidak dikenal. Pilih salah satu: [ranks]' not-possible: 'Peringkat harus lebih tinggi dari pengunjung.' - rank-set: >- - Peringkat ditetapkan dari [from] ke [to] di pulau - [name]. + cannot-set-owner: 'Kamu tidak dapat mengatur atau mengubah peringkat pemilik. Gunakan setowner untuk memindahkan kepemilikan.' + already-rank: '[name] sudah [rank] di [prefix_island] ini.' + rank-set: 'Peringkat ditetapkan dari [from] ke [to] di pulau [name].' + admin-changed-rank: 'Admin mengatur peringkatmu di [prefix_island] milik [name] menjadi [rank].' setprotectionlocation: parameters: '[koord x y z]' description: >- diff --git a/src/main/resources/locales/it.yml b/src/main/resources/locales/it.yml index 738aaa42a..2647edd7b 100644 --- a/src/main/resources/locales/it.yml +++ b/src/main/resources/locales/it.yml @@ -406,11 +406,14 @@ commands: description: ottieni il rango di un giocatore nella sua isola rank-is: 'Il rango è [rank] nella sua isola.' setrank: - parameters: - description: imposta il rango di un player nella sua isola - unknown-rank: 'Rango sconosciuto!' + parameters: [proprietario dell’[prefix_island] | x,y,z] + description: 'promuove o degrada un giocatore a un grado sull’[prefix_island] del suo team, o sull’[prefix_island] indicata dal proprietario o dal centro x,y,z' + unknown-rank: 'Grado sconosciuto [rank]. Scegli uno tra: [ranks]' not-possible: 'Il rank deve essere maggiore di quello del visitatore' + cannot-set-owner: 'Non puoi impostare o cambiare il grado del proprietario. Usa setowner per trasferire la proprietà.' + already-rank: '[name] è già [rank] su questa [prefix_island].' rank-set: 'Rango impostato da [from] a [to].' + admin-changed-rank: 'Un admin ha impostato il tuo grado sull’[prefix_island] di [name] a [rank].' setprotectionlocation: parameters: '[coordinate x y z]' description: >- diff --git a/src/main/resources/locales/ja.yml b/src/main/resources/locales/ja.yml index 6f2d80b09..7962dae25 100644 --- a/src/main/resources/locales/ja.yml +++ b/src/main/resources/locales/ja.yml @@ -348,11 +348,14 @@ commands: description: プレイヤーの島でのランクを見る。 rank-is: プレーヤーのランクは [rank]である。 setrank: - parameters: <プレーヤー> <ランク> - description: プレイヤーのランクを設定する - unknown-rank: '不明ランク!' + parameters: <プレイヤー> <ランク> [[prefix_island]の所有者 | x,y,z] + description: 'プレイヤーをチームの[prefix_island]、または所有者名か中心座標 x,y,z で指定した[prefix_island]で、指定のランクに昇格または降格します' + unknown-rank: '不明なランク [rank] です。次から選んでください: [ranks]' not-possible: 'ランクは訪問者より高くなければなりません' + cannot-set-owner: '所有者のランクは設定・変更できません。所有権を移すには setowner を使用してください。' + already-rank: '[name] はこの[prefix_island]ですでに [rank] です。' rank-set: '[from] から [to] に設定されたランク。' + admin-changed-rank: '管理者が [name] の[prefix_island]でのあなたのランクを [rank] に設定しました。' setprotectionlocation: parameters: '[x y z coords]' description: 現在地または[x y z]を島の保護地域の中心として設定します diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml index 54c18ec93..0d3520c2e 100644 --- a/src/main/resources/locales/ko.yml +++ b/src/main/resources/locales/ko.yml @@ -365,11 +365,14 @@ commands: description: 자신이나 소유자의 섬의 랭크를 가져옵니다 rank-is: '[name]의 섬에서의 순위는 [rank]입니다' setrank: - parameters: [island owner] - description: 섬의 플레이어의 랭크를 설정합니다 - unknown-rank: '알수없는 랭크입니다' + parameters: <플레이어> <등급> [[prefix_island] 소유자 | x,y,z] + description: '플레이어를 자신의 팀 [prefix_island], 또는 소유자 이름이나 중심 x,y,z로 지정한 [prefix_island]에서 지정한 등급으로 승급 또는 강등합니다' + unknown-rank: '알 수 없는 등급 [rank]입니다. 다음 중 하나를 선택하세요: [ranks]' not-possible: '랭크는 방문자보다 높아야합니다.' + cannot-set-owner: '소유자의 등급은 설정하거나 변경할 수 없습니다. 소유권을 이전하려면 setowner를 사용하세요.' + already-rank: '[name]님은 이미 이 [prefix_island]에서 [rank]입니다.' rank-set: '랭크를 [from] 에서 [to] 로 [name]의 섬에서 설정했습니다' + admin-changed-rank: '관리자가 [name]님의 [prefix_island]에서 당신의 등급을 [rank](으)로 설정했습니다.' setprotectionlocation: parameters: '[x y z 좌표]' description: 현재 위치 또는 [x y z]를 [prefix_island]의 보호 지역 중심으로 설정합니다. diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index 31c2b1383..728783ba5 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -396,11 +396,14 @@ commands: description: atgriež spēlētāja rangu komandā rank-is: 'Spēlētājam ir [rank] rangs uz viņa salas.' setrank: - parameters: - description: uzstāda spēlētājam rangu uz viņa salas - unknown-rank: 'Nezināms rangs!' + parameters: [[prefix_island] īpašnieks | x,y,z] + description: 'paaugstina vai pazemina spēlētāju uz rangu viņa komandas [prefix_island] vai [prefix_island], kas norādīta pēc īpašnieka vai centra x,y,z' + unknown-rank: 'Nezināms rangs [rank]. Izvēlies vienu no: [ranks]' not-possible: 'Rangam ir jābūt lielākam par apmeklētāju' + cannot-set-owner: 'Īpašnieka rangu nevar iestatīt vai mainīt. Izmanto setowner, lai nodotu īpašumtiesības.' + already-rank: '[name] jau ir [rank] šajā [prefix_island].' rank-set: 'Spēlētājam nomainīts rangs no [from] uz [to].' + admin-changed-rank: 'Administrators iestatīja tavu rangu [name] [prefix_island] uz [rank].' setprotectionlocation: parameters: '[x y z koordinātas]' description: >- diff --git a/src/main/resources/locales/nl.yml b/src/main/resources/locales/nl.yml index b8da992f7..b1fdcba27 100644 --- a/src/main/resources/locales/nl.yml +++ b/src/main/resources/locales/nl.yml @@ -414,15 +414,14 @@ commands: eigenaar rank-is: 'rang is [rank] op [name] ''s eiland.' setrank: - parameters: [eilandeigenaar] - description: >- - bepaal de rang van een speler op zijn of haar eiland of op het eiland - van de eigenaar - unknown-rank: 'Onbekende rang!' + parameters: [[prefix_island] eigenaar | x,y,z] + description: 'promoveer of degradeer een speler naar een rang op het [prefix_island] van zijn team, of op het [prefix_island] aangeduid met de eigenaar of het x,y,z-middelpunt' + unknown-rank: 'Onbekende rang [rank]. Kies uit: [ranks]' not-possible: 'Rang moet hoger zijn dan bezoeker.' - rank-set: >- - Rang ingesteld van [from] naar [to] op [name] 's - eiland. + cannot-set-owner: 'Je kunt de rang van de eigenaar niet instellen of wijzigen. Gebruik setowner om het eigendom over te dragen.' + already-rank: '[name] is al [rank] op dit [prefix_island].' + rank-set: 'Rang ingesteld van [from] naar [to] op [name] ''s eiland.' + admin-changed-rank: 'Een admin heeft je rang op het [prefix_island] van [name] ingesteld op [rank].' setprotectionlocation: parameters: '[x y z coördinaten]' description: >- diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml index 47e70f756..4c4aa3b61 100644 --- a/src/main/resources/locales/pl.yml +++ b/src/main/resources/locales/pl.yml @@ -398,11 +398,14 @@ commands: description: zdobądź role gracza na jego wyspie rank-is: 'Rola [rank] na jego wyspie.' setrank: - parameters: - description: ustawia role gracza na swojej wyspie - unknown-rank: 'Nieznana rola!' + parameters: [właściciel [prefix_island] | x,y,z] + description: 'awansuje lub degraduje gracza do rangi na [prefix_island] jego drużyny, lub na [prefix_island] wskazanej przez właściciela lub środek x,y,z' + unknown-rank: 'Nieznana ranga [rank]. Wybierz jedną z: [ranks]' not-possible: 'Ranga musi być wyższa niż ranga gościa.' + cannot-set-owner: 'Nie możesz ustawić ani zmienić rangi właściciela. Użyj setowner, aby przenieść własność.' + already-rank: '[name] ma już rangę [rank] na tej [prefix_island].' rank-set: 'Rola ustawiona z [from] na [to].' + admin-changed-rank: 'Administrator ustawił twoją rangę na [prefix_island] gracza [name] na [rank].' setprotectionlocation: parameters: '[x y z współrzędne]' description: >- diff --git a/src/main/resources/locales/pt-BR.yml b/src/main/resources/locales/pt-BR.yml index 9e52e7468..3a8320217 100644 --- a/src/main/resources/locales/pt-BR.yml +++ b/src/main/resources/locales/pt-BR.yml @@ -392,15 +392,14 @@ commands: description: ver o cargo de um jogador em sua ilha ou na ilha de outro rank-is: 'Cargo é [rank] na ilha de [name].' setrank: - parameters: [dono da ilha] - description: >- - alterar o cargo de um jogador em sua própria ilha ou na ilha do jogador - especificado - unknown-rank: 'Cargo desconhecido!' + parameters: [dono da [prefix_island] | x,y,z] + description: 'promove ou rebaixa um jogador para um cargo na [prefix_island] da sua equipe, ou na [prefix_island] indicada pelo dono ou centro x,y,z' + unknown-rank: 'Cargo desconhecido [rank]. Escolha um de: [ranks]' not-possible: 'O cargo deve ser maior que visitante.' - rank-set: >- - Cargo alterado de [from] para [to] na ilha de - [name]. + cannot-set-owner: 'Você não pode definir nem mudar o cargo do dono. Use setowner para transferir a propriedade.' + already-rank: '[name] já é [rank] nesta [prefix_island].' + rank-set: 'Cargo alterado de [from] para [to] na ilha de [name].' + admin-changed-rank: 'Um admin definiu seu cargo na [prefix_island] de [name] como [rank].' setprotectionlocation: parameters: As coordenadas [x y z] description: >- diff --git a/src/main/resources/locales/pt.yml b/src/main/resources/locales/pt.yml index 0c057142c..a9b2453d6 100644 --- a/src/main/resources/locales/pt.yml +++ b/src/main/resources/locales/pt.yml @@ -397,13 +397,14 @@ commands: description: obter o cargo de um jogador em sua ilha ou na ilha do dono rank-is: 'Cargo é [rank] na ilha de [name].' setrank: - parameters: [dono da ilha] - description: definir o cargo de um jogador em sua ilha ou na ilha do dono - unknown-rank: 'Cargo desconhecido!' + parameters: [dono da [prefix_island] | x,y,z] + description: 'promove ou despromove um jogador para um cargo na [prefix_island] da sua equipa, ou na [prefix_island] indicada pelo dono ou centro x,y,z' + unknown-rank: 'Cargo desconhecido [rank]. Escolhe um de: [ranks]' not-possible: 'O cargo deve ser maior que visitante.' - rank-set: >- - Cargo definido de [from] para [to] na ilha de - [name]. + cannot-set-owner: 'Não podes definir nem mudar o cargo do dono. Usa setowner para transferir a propriedade.' + already-rank: '[name] já é [rank] nesta [prefix_island].' + rank-set: 'Cargo definido de [from] para [to] na ilha de [name].' + admin-changed-rank: 'Um admin definiu o teu cargo na [prefix_island] de [name] como [rank].' setprotectionlocation: parameters: '[coordenadas x y z]' description: >- diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml index 4632d6e5d..1540eada4 100644 --- a/src/main/resources/locales/ro.yml +++ b/src/main/resources/locales/ro.yml @@ -405,11 +405,14 @@ commands: description: obține rangul unui jucător pe insula lor sau pe insula proprietarului rank-is: 'Rank este [rank] pe insula [name] .' setrank: - parameters: [proprietarul insulei] - description: stabiliți rangul unui jucător pe insula lor sau pe insula proprietarului - unknown-rank: 'Rang necunoscut!' + parameters: [proprietarul [prefix_island] | x,y,z] + description: 'promovează sau retrogradează un jucător la un rang pe [prefix_island] echipei sale, sau pe [prefix_island] indicată prin proprietar sau centrul x,y,z' + unknown-rank: 'Rang necunoscut [rank]. Alege unul dintre: [ranks]' not-possible: 'Rank trebuie să fie mai mare decât vizitatorul.' + cannot-set-owner: 'Nu poți seta sau schimba rangul proprietarului. Folosește setowner pentru a transfera proprietatea.' + already-rank: '[name] este deja [rank] pe această [prefix_island].' rank-set: 'Rang setat de la [from] la [to] pe insula [name] .' + admin-changed-rank: 'Un admin ți-a setat rangul pe [prefix_island] lui [name] la [rank].' setprotectionlocation: parameters: '[ coordonatele x y z ]' description: >- diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml index 7d459a10d..580d46ab6 100644 --- a/src/main/resources/locales/ru.yml +++ b/src/main/resources/locales/ru.yml @@ -395,12 +395,14 @@ commands: description: отображает ранг игрока на его острове или острове владельца rank-is: Ранг [rank] на острове игрока [name]. setrank: - parameters: <игрок> <ранг> [владелец [prefix_island]а] - description: устанавливает ранг игрока на их острове или острове владельца - unknown-rank: Неизвестный ранг! - not-possible: Ранг должен быть больше чем у посетителя. - rank-set: Ранг установлен между [from] и [to] на - острове игрока [name]. + parameters: <игрок> <ранг> [владелец [prefix_island]а | x,y,z] + description: 'повысить или понизить игрока до ранга на [prefix_island]е его команды, либо на [prefix_island]е, указанном по владельцу или центру x,y,z' + unknown-rank: 'Неизвестный ранг [rank]. Выберите один из: [ranks]' + not-possible: 'Ранг должен быть больше чем у посетителя.' + cannot-set-owner: 'Нельзя установить или изменить ранг владельца. Используйте setowner, чтобы передать владение.' + already-rank: '[name] уже имеет ранг [rank] на этом [prefix_island]е.' + rank-set: 'Ранг установлен между [from] и [to] на острове игрока [name].' + admin-changed-rank: 'Администратор установил ваш ранг на [prefix_island]е игрока [name]: [rank].' setprotectionlocation: parameters: '[x y z координаты]' description: устанавливает текущую локацию или координаты [x y z] как центральную diff --git a/src/main/resources/locales/tr.yml b/src/main/resources/locales/tr.yml index bb8e9ce3a..5155312ed 100644 --- a/src/main/resources/locales/tr.yml +++ b/src/main/resources/locales/tr.yml @@ -394,11 +394,14 @@ commands: description: Olduğun ada üstündeki oyuncunun rankını gösterir. rank-is: 'Ada üstündeki yetkisi: [rank].' setrank: - parameters: - description: Olduğun ada üstündeki oyuncuya yetkisini ayarlar - unknown-rank: 'Bilinmeyen rütbe!' + parameters: [[prefix_island] sahibi | x,y,z] + description: 'bir oyuncuyu takımının [prefix_island] üzerinde veya sahibiyle ya da x,y,z merkeziyle belirtilen [prefix_island] üzerinde bir rütbeye yükseltir veya düşürür' + unknown-rank: 'Bilinmeyen rütbe [rank]. Şunlardan birini seçin: [ranks]' not-possible: 'Rütbe türü ziyaretçiden yüksek olmalıdır.' + cannot-set-owner: 'Sahibin rütbesini ayarlayamaz veya değiştiremezsiniz. Sahipliği aktarmak için setowner kullanın.' + already-rank: '[name] bu [prefix_island] üzerinde zaten [rank].' rank-set: 'Rütbe [from]''dan [to] yükseltildi.' + admin-changed-rank: 'Bir yönetici [name] adlı oyuncunun [prefix_island] üzerindeki rütbeni [rank] olarak ayarladı.' setprotectionlocation: parameters: '[x y z koordinatları]' description: >- diff --git a/src/main/resources/locales/uk.yml b/src/main/resources/locales/uk.yml index 7c6fd5730..74b1bb3ff 100644 --- a/src/main/resources/locales/uk.yml +++ b/src/main/resources/locales/uk.yml @@ -356,11 +356,14 @@ commands: description: отримати ранг гравця на його [prefix_island] або на [prefix_island] власника rank-is: 'Ранг [rank] на [prefix_island] гравця [name].' setrank: - parameters: <гравець> [[prefix_island] owner] - description: встановити ранг гравцю на його [prefix_island] або на [prefix_island] власника - unknown-rank: 'Невідомий ранг!' + parameters: <гравець> <ранг> [власник [prefix_island]а | x,y,z] + description: 'підвищити або понизити гравця до рангу на [prefix_island]і його команди, або на [prefix_island]і, вказаному за власником чи центром x,y,z' + unknown-rank: 'Невідомий ранг [rank]. Виберіть один із: [ranks]' not-possible: 'Ранг має бути вищий за «відвідувач».' + cannot-set-owner: 'Не можна встановити чи змінити ранг власника. Використайте setowner, щоб передати власність.' + already-rank: '[name] уже має ранг [rank] на цьому [prefix_island]і.' rank-set: 'Ранг змінено з [from] на [to] на [prefix_island] [name].' + admin-changed-rank: 'Адміністратор встановив ваш ранг на [prefix_island]і гравця [name]: [rank].' setprotectionlocation: parameters: '[x y z coords]' description: встановити поточну локацію або [x y z] центром зони захисту [prefix_island] diff --git a/src/main/resources/locales/vi.yml b/src/main/resources/locales/vi.yml index bda1a91de..51b942157 100644 --- a/src/main/resources/locales/vi.yml +++ b/src/main/resources/locales/vi.yml @@ -395,11 +395,14 @@ commands: description: xem hạng của người chơi trên đảo của họ hoặc đảo của chủ rank-is: 'Hạng là [rank] trên đảo của [name].' setrank: - parameters: [chủ đảo] - description: đặt hạng của người chơi trên đảo c��a họ hoặc đảo của chủ - unknown-rank: 'Hạng không xác định!' + parameters: [chủ [prefix_island] | x,y,z] + description: 'thăng hoặc giáng cấp một người chơi lên một cấp bậc trên [prefix_island] của đội họ, hoặc trên [prefix_island] được chỉ định theo chủ sở hữu hoặc tâm x,y,z' + unknown-rank: 'Cấp bậc không xác định [rank]. Chọn một trong: [ranks]' not-possible: 'Hạng phải cao hơn khách tham quan.' + cannot-set-owner: 'Bạn không thể đặt hoặc thay đổi cấp bậc của chủ đảo. Dùng setowner để chuyển quyền sở hữu.' + already-rank: '[name] đã là [rank] trên [prefix_island] này.' rank-set: 'Đã đặt hạng t�� [from] thành [to] trên đảo của [name].' + admin-changed-rank: 'Quản trị viên đã đặt cấp bậc của bạn trên [prefix_island] của [name] thành [rank].' setprotectionlocation: parameters: '[toạ độ x y z]' description: đặt vị trí hiện tại hoặc [x y z] là điểm giữa khu vực bảo vệ diff --git a/src/main/resources/locales/zh-CN.yml b/src/main/resources/locales/zh-CN.yml index 3f40d6756..a2ada9a83 100644 --- a/src/main/resources/locales/zh-CN.yml +++ b/src/main/resources/locales/zh-CN.yml @@ -356,11 +356,14 @@ commands: description: 获取指定玩家的身份等级 rank-is: '玩家[name]在岛屿上的身份等级为: [rank].' setrank: - parameters: [island owner] - description: 设置指定玩家的身份等级 - unknown-rank: '未知的身份等级!' + parameters: <玩家> <等级> [[prefix_island]岛主 | x,y,z] + description: '将玩家在其团队[prefix_island]上、或在通过岛主名称或中心 x,y,z 指定的[prefix_island]上提升或降低到指定等级' + unknown-rank: '未知的身份等级 [rank]。请选择以下之一: [ranks]' not-possible: '身份等级必须比访客高.' + cannot-set-owner: '无法设置或更改岛主的身份等级。请使用 setowner 转移所有权。' + already-rank: '[name] 在此[prefix_island]上已经是 [rank]。' rank-set: '已将玩家[name]的身份等级从[from]设置为[to].' + admin-changed-rank: '管理员已将你在 [name] 的[prefix_island]上的身份等级设置为 [rank]。' setprotectionlocation: parameters: '[x y z coords]' description: 将当前位置或指定坐标作为岛屿保护的中心点 diff --git a/src/main/resources/locales/zh-HK.yml b/src/main/resources/locales/zh-HK.yml index 440089bd1..3c84a9f11 100644 --- a/src/main/resources/locales/zh-HK.yml +++ b/src/main/resources/locales/zh-HK.yml @@ -357,11 +357,14 @@ commands: description: 得到玩家在他們的島嶼上的階銜 rank-is: '在他們的島嶼上的階銜是 [rank]。' setrank: - parameters: - description: 設置玩家在他們島嶼上的階銜 - unknown-rank: '未知階銜!' + parameters: <玩家> <等級> [[prefix_island]島主 | x,y,z] + description: '將玩家在其團隊[prefix_island]上、或在透過島主名稱或中心 x,y,z 指定的[prefix_island]上提升或降低到指定等級' + unknown-rank: '未知的身份等級 [rank]。請選擇以下之一: [ranks]' not-possible: '要設置的階銜必須比 “訪客” 高。' + cannot-set-owner: '無法設定或更改島主的身份等級。請使用 setowner 轉移擁有權。' + already-rank: '[name] 在此[prefix_island]上已經是 [rank]。' rank-set: '階銜由 [from] 設置為 [to]。' + admin-changed-rank: '管理員已將你在 [name] 的[prefix_island]上的身份等級設定為 [rank]。' setprotectionlocation: parameters: '[x y z (坐標)]' description: 將所處位置或給定坐標設置為島嶼保護區的中心點 diff --git a/src/main/resources/locales/zh-TW.yml b/src/main/resources/locales/zh-TW.yml index ed128444e..5ee56f7bf 100644 --- a/src/main/resources/locales/zh-TW.yml +++ b/src/main/resources/locales/zh-TW.yml @@ -331,11 +331,14 @@ commands: description: 查詢玩家在自己的[prefix_island]或指定島主[prefix_island]中的權限等級 rank-is: 權限等級為 [rank] ([name] 的[prefix_island])。 setrank: - parameters: <玩家> <權限等級> [[prefix_island]島主] - description: 設定玩家在自己的[prefix_island]或指定島主[prefix_island]中的權限等級 - unknown-rank: 未知的權限等級! - not-possible: 權限等級必須高於訪客。 - rank-set: 已將 [name] 的[prefix_island]權限等級從 [from] 變更為 [to]。 + parameters: <玩家> <等級> [[prefix_island]島主 | x,y,z] + description: '將玩家在其團隊[prefix_island]上、或在透過島主名稱或中心 x,y,z 指定的[prefix_island]上提升或降低到指定等級' + unknown-rank: '未知的身份等級 [rank]。請選擇以下之一: [ranks]' + not-possible: '權限等級必須高於訪客。' + cannot-set-owner: '無法設定或更改島主的身份等級。請使用 setowner 轉移擁有權。' + already-rank: '[name] 在此[prefix_island]上已經是 [rank]。' + rank-set: '已將 [name] 的[prefix_island]權限等級從 [from] 變更為 [to]。' + admin-changed-rank: '管理員已將你在 [name] 的[prefix_island]上的身份等級設定為 [rank]。' setprotectionlocation: parameters: '[x y z 座標]' description: 將目前位置或指定的 [x y z] 座標設為[prefix_island]的保護區域中心 diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommandTest.java index deae84001..721a714df 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminSetrankCommandTest.java @@ -5,17 +5,20 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; -import org.bukkit.entity.Player; +import org.bukkit.Location; +import org.bukkit.util.Vector; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -23,6 +26,7 @@ import world.bentobox.bentobox.RanksManagerTestSetup; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.PlayersManager; @@ -35,167 +39,330 @@ */ class AdminSetrankCommandTest extends RanksManagerTestSetup { + private static final String TARGET = "target"; + private static final String OWNER = "owner"; + private static final String XYZ = "0,0,0"; + private static final String XYZ2 = "100,64,100"; + @Mock private CompositeCommand ac; @Mock private User user; @Mock private PlayersManager pm; + @Mock + private Island island2; private AdminSetrankCommand c; private UUID targetUUID; + private UUID ownerUUID; - @Override + @Override @BeforeEach public void setUp() throws Exception { super.setUp(); Util.setPlugin(plugin); + User.setPlugin(plugin); + + when(ac.getWorld()).thenReturn(world); // Players Manager when(plugin.getPlayers()).thenReturn(pm); - - // Target targetUUID = UUID.randomUUID(); - Player p = mock(Player.class); - when(p.getUniqueId()).thenReturn(targetUUID); - User.getInstance(p); + ownerUUID = UUID.randomUUID(); + when(pm.getUUID(TARGET)).thenReturn(targetUUID); + when(pm.getUUID(OWNER)).thenReturn(ownerUUID); + when(pm.getName(ownerUUID)).thenReturn(OWNER); + + // island2 is a team island owned by "owner"; the target is a plain member of it + when(island2.getOwner()).thenReturn(ownerUUID); + when(island2.getCenter()).thenReturn(location); + when(location.toVector()).thenReturn(new Vector(0, 0, 0)); + when(island2.getRank(targetUUID)).thenReturn(RanksManager.MEMBER_RANK); + when(im.getIslands(world, targetUUID)).thenReturn(List.of(island2)); + when(im.getIslands(world, ownerUUID)).thenReturn(List.of(island2)); // Online players mockedUtil.when(() -> Util.getOnlinePlayerList(any())).thenReturn(Collections.singletonList("tastybento")); mockedUtil.when(() -> Util.getUUID(anyString())).thenCallRealMethod(); + mockedUtil.when(() -> Util.xyz(any())).thenCallRealMethod(); + mockedUtil.when(() -> Util.tabLimit(any(), any())).thenCallRealMethod(); + mockedUtil.when(() -> Util.stripColor(anyString())).thenCallRealMethod(); // Translations when(user.getTranslation(anyString())) .thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + // Ranks: the mocked manager returns the reference for each value + when(rm.getRank(RanksManager.MEMBER_RANK)).thenReturn(RanksManager.MEMBER_RANK_REF); + when(rm.getRank(RanksManager.SUB_OWNER_RANK)).thenReturn(RanksManager.SUB_OWNER_RANK_REF); + when(rm.getRank(RanksManager.TRUSTED_RANK)).thenReturn(RanksManager.TRUSTED_RANK_REF); + // Command c = new AdminSetrankCommand(ac); - } /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#AdminSetrankCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + * Adds a second team island that the target is also a member of. */ + private void addSecondIsland(UUID owner) { + when(island.getOwner()).thenReturn(owner); + Location loc2 = mock(Location.class); + when(loc2.toVector()).thenReturn(new Vector(100, 64, 100)); + when(island.getCenter()).thenReturn(loc2); + when(island.getRank(targetUUID)).thenReturn(RanksManager.SUB_OWNER_RANK); + when(im.getIslands(world, targetUUID)).thenReturn(List.of(island, island2)); + when(im.getIslands(world, owner)).thenReturn(List.of(island, island2)); + } + @Test void testAdminSetrankCommand() { assertEquals("setrank", c.getLabel()); } - /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#setup()}. - */ @Test void testSetup() { assertEquals("admin.setrank", c.getPermission()); assertFalse(c.isOnlyPlayer()); assertEquals("commands.admin.setrank.parameters", c.getParameters()); assertEquals("commands.admin.setrank.description", c.getDescription()); - } - /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test void testCanExecuteNoArgs() { assertFalse(c.canExecute(user, "", Collections.emptyList())); - verify(user).getTranslation("commands.help.console"); + verify(user).getTranslation("commands.admin.setrank.description"); } - /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test void testCanExecuteOneArg() { assertFalse(c.canExecute(user, "", Collections.singletonList("test"))); - verify(user).getTranslation("commands.help.console"); + verify(user).getTranslation("commands.admin.setrank.description"); + } + + @Test + void testCanExecuteTooManyArgs() { + assertFalse(c.canExecute(user, "", List.of("a", "b", "c", "d"))); + verify(user).getTranslation("commands.admin.setrank.description"); } - /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test void testCanExecuteUnknownPlayer() { - assertFalse(c.canExecute(user, "", Arrays.asList("tastybento", "member"))); + assertFalse(c.canExecute(user, "", List.of("tastybento", "member"))); verify(user).sendMessage("general.errors.unknown-player", "[name]", "tastybento"); } - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test - void testCanExecuteKnownPlayerNoIsland() { - when(pm.getUUID(any())).thenReturn(targetUUID); - assertFalse(c.canExecute(user, "", Arrays.asList("tastybento", "ranks.member"))); - verify(user).sendMessage("general.errors.player-has-no-island"); + void testCanExecuteUnknownRank() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "xxx"))); + verify(user).sendMessage("commands.admin.setrank.unknown-rank", TextVariables.RANK, "xxx", "[ranks]", + "coop, trusted, member, sub-owner"); } - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test - void testCanExecuteKnownPlayerHasIslandUnknownRank() { - when(pm.getUUID(any())).thenReturn(targetUUID); - when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); - assertFalse(c.canExecute(user, "", Arrays.asList("tastybento", "xxx"))); - verify(user).sendMessage("commands.admin.setrank.unknown-rank"); + void testCanExecuteTooLowRank() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "visitor"))); + verify(user).sendMessage("commands.admin.setrank.not-possible"); } - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test - void testCanExecuteKnownPlayerHasIslandTooLowRank() { - when(pm.getUUID(any())).thenReturn(targetUUID); - when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); - assertFalse(c.canExecute(user, "", Arrays.asList("tastybento", "ranks.visitor"))); + void testCanExecuteBannedRank() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "banned"))); verify(user).sendMessage("commands.admin.setrank.not-possible"); } - /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. - */ @Test - void testCanExecuteKnownPlayerHasIslandSuccess() { - when(pm.getUUID(any())).thenReturn(targetUUID); - when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); - assertTrue(c.canExecute(user, "", Arrays.asList("tastybento", "ranks.member"))); + void testCanExecuteOwnerRankRefused() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "owner"))); + verify(user).sendMessage("commands.admin.setrank.cannot-set-owner"); + } + + @Test + void testCanExecuteAdminRankRefused() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "admin"))); + verify(user).sendMessage("commands.admin.setrank.cannot-set-owner"); } /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * The legacy form - the rank's locale reference - still works because the mocked translation + * returns the reference itself. */ @Test - void testExecuteUserStringListOfString() { - // Set the target - testCanExecuteKnownPlayerHasIslandSuccess(); - Island island = mock(Island.class); - when(island.getRank(any(User.class))).thenReturn(RanksManager.SUB_OWNER_RANK); - when(im.getIsland(any(), any(UUID.class))).thenReturn(island); - when(island.getCenter()).thenReturn(location); - assertTrue(c.execute(user, "", Arrays.asList("tastybento", "member"))); - verify(user).sendMessage("commands.admin.setrank.rank-set", "[from]", "", "[to]", "", "[name]", null); + void testCanExecuteRankByReference() { + assertTrue(c.canExecute(user, "", List.of(TARGET, "ranks.sub-owner"))); + } + + @Test + void testCanExecuteRankByTranslatedName() { + when(user.getTranslation(RanksManager.SUB_OWNER_RANK_REF)).thenReturn("§bSous-Chef"); + assertTrue(c.canExecute(user, "", List.of(TARGET, "SOUS-chef"))); + } + + @Test + void testCanExecuteRankByNumber() { + assertTrue(c.canExecute(user, "", List.of(TARGET, "900"))); + } + + @Test + void testCanExecuteNoIslandKnownPlayer() { + when(im.getIslands(world, targetUUID)).thenReturn(List.of()); + assertFalse(c.canExecute(user, "", List.of(TARGET, "member"))); + verify(user).sendMessage("general.errors.player-has-no-island"); + } + + @Test + void testCanExecuteTargetOnlyOwnsIsland() { + when(island2.getOwner()).thenReturn(targetUUID); + when(im.hasIsland(world, targetUUID)).thenReturn(true); + assertFalse(c.canExecute(user, "", List.of(TARGET, "member"))); + verify(user).sendMessage("commands.admin.setrank.cannot-set-owner"); + } + + @Test + void testCanExecuteAlreadyRank() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "member"))); + verify(user).sendMessage("commands.admin.setrank.already-rank", TextVariables.NAME, TARGET, + TextVariables.RANK, RanksManager.MEMBER_RANK_REF); + } + + @Test + void testCanExecuteSingleIslandSuccess() { + assertTrue(c.canExecute(user, "", List.of(TARGET, "sub-owner"))); + } + + @Test + void testCanExecuteMultipleIslandsRequiresIsland() { + addSecondIsland(UUID.randomUUID()); + assertFalse(c.canExecute(user, "", List.of(TARGET, "sub-owner"))); + verify(user).sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island"); + verify(user).sendMessage("commands.admin.unregister.errors.specify-island-location", TextVariables.XYZ, XYZ); + verify(user).sendMessage("commands.admin.unregister.errors.specify-island-location", TextVariables.XYZ, XYZ2); + } + + @Test + void testCanExecuteIslandByOwner() { + addSecondIsland(UUID.randomUUID()); + assertTrue(c.canExecute(user, "", List.of(TARGET, "sub-owner", OWNER))); + assertTrue(c.execute(user, "", List.of(TARGET, "sub-owner", OWNER))); + verify(island2).setRank(targetUUID, RanksManager.SUB_OWNER_RANK); + verify(island, never()).setRank(any(UUID.class), any(Integer.class)); + } + + @Test + void testCanExecuteIslandByOwnerUnknownPlayer() { + assertFalse(c.canExecute(user, "", List.of(TARGET, "sub-owner", "stranger"))); + verify(user).sendMessage("general.errors.unknown-player", TextVariables.NAME, "stranger"); + } + + @Test + void testCanExecuteIslandByOwnerNotOwner() { + when(im.getIslands(world, ownerUUID)).thenReturn(List.of()); + assertFalse(c.canExecute(user, "", List.of(TARGET, "sub-owner", OWNER))); + verify(user).sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, OWNER); + } + + @Test + void testCanExecuteIslandByOwnerWithConcurrentIslands() { + addSecondIsland(ownerUUID); + assertFalse(c.canExecute(user, "", List.of(TARGET, "sub-owner", OWNER))); + verify(user).sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island"); + verify(user, times(2)).sendMessage(eq("commands.admin.unregister.errors.specify-island-location"), + eq(TextVariables.XYZ), any()); + } + + @Test + void testCanExecuteIslandByXyz() { + addSecondIsland(ownerUUID); + when(im.getIslandAt(any())).thenAnswer(inv -> { + Location l = inv.getArgument(0); + return l.getBlockX() == 100 ? Optional.of(island) : Optional.of(island2); + }); + assertTrue(c.canExecute(user, "", List.of(TARGET, "member", XYZ2))); + assertTrue(c.execute(user, "", List.of(TARGET, "member", XYZ2))); + verify(island).setRank(targetUUID, RanksManager.MEMBER_RANK); + verify(island2, never()).setRank(any(UUID.class), any(Integer.class)); + } + + @Test + void testCanExecuteIslandByXyzUnknown() { + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + assertFalse(c.canExecute(user, "", List.of(TARGET, "member", "9,9,9"))); + verify(user).sendMessage("commands.admin.unregister.errors.unknown-island-location"); + } + + @Test + void testCanExecuteIslandByXyzTargetIsOwner() { + when(island2.getOwner()).thenReturn(targetUUID); + when(im.getIslandAt(any())).thenReturn(Optional.of(island2)); + assertFalse(c.canExecute(user, "", List.of(TARGET, "member", XYZ))); + verify(user).sendMessage("commands.admin.setrank.cannot-set-owner"); } /** - * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.AdminSetrankCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Granting a rank on an island the target has no standing on yet, e.g. making them trusted. */ @Test - void testTabCompleteUserStringListOfString() { - Optional> result = c.tabComplete(user, "", Arrays.asList("setrank", "")); + void testExecuteGrantTrustedOnOwnersIsland() { + when(im.getIslands(world, targetUUID)).thenReturn(List.of()); + when(island2.getRank(targetUUID)).thenReturn(RanksManager.VISITOR_RANK); + assertTrue(c.canExecute(user, "", List.of(TARGET, "trusted", OWNER))); + assertTrue(c.execute(user, "", List.of(TARGET, "trusted", OWNER))); + verify(island2).setRank(targetUUID, RanksManager.TRUSTED_RANK); + } + + @Test + void testExecutePromote() { + assertTrue(c.canExecute(user, "", List.of(TARGET, "sub-owner"))); + assertTrue(c.execute(user, "", List.of(TARGET, "sub-owner"))); + verify(island2).setRank(targetUUID, RanksManager.SUB_OWNER_RANK); + verify(user).sendMessage("commands.admin.setrank.rank-set", "[from]", RanksManager.MEMBER_RANK_REF, "[to]", + RanksManager.SUB_OWNER_RANK_REF, TextVariables.NAME, OWNER); + // IslandEvent.build fires the generic IslandEvent plus the specific rank change event + verify(pim, times(2)).callEvent(any()); + } + + @Test + void testExecuteDemote() { + when(island2.getRank(targetUUID)).thenReturn(RanksManager.SUB_OWNER_RANK); + assertTrue(c.canExecute(user, "", List.of(TARGET, "member"))); + assertTrue(c.execute(user, "", List.of(TARGET, "member"))); + verify(island2).setRank(targetUUID, RanksManager.MEMBER_RANK); + verify(user).sendMessage("commands.admin.setrank.rank-set", "[from]", RanksManager.SUB_OWNER_RANK_REF, "[to]", + RanksManager.MEMBER_RANK_REF, TextVariables.NAME, OWNER); + verify(pim, times(2)).callEvent(any()); + } + + @Test + void testTabCompletePlayer() { + Optional> result = c.tabComplete(user, "", List.of("")); assertTrue(result.isPresent()); - result.ifPresent(list -> { - assertEquals(1, list.size()); - assertEquals("tastybento", list.getFirst()); - }); + assertEquals(List.of("tastybento"), result.get()); } + @Test + void testTabCompleteRank() { + Optional> result = c.tabComplete(user, "", List.of(TARGET, "")); + assertTrue(result.isPresent()); + assertEquals(List.of("coop", "trusted", "member", "sub-owner"), result.get()); + } + + @Test + void testTabCompleteRankPartial() { + Optional> result = c.tabComplete(user, "", List.of(TARGET, "s")); + assertTrue(result.isPresent()); + assertEquals(List.of("sub-owner"), result.get()); + } + + @Test + void testTabCompleteIsland() { + Optional> result = c.tabComplete(user, "", List.of(TARGET, "member", "")); + assertTrue(result.isPresent()); + assertTrue(result.get().containsAll(List.of("tastybento", XYZ))); + } + + @Test + void testTabCompleteTooManyArgs() { + assertTrue(c.tabComplete(user, "", List.of(TARGET, "member", OWNER, "")).isEmpty()); + } }