From 3c182140166c525028de8efe06e5f020c471cdd6 Mon Sep 17 00:00:00 2001 From: William Date: Sun, 16 Apr 2023 16:48:08 +0100 Subject: [PATCH] Add ability to resolve unsafe positions by UUID where the identifier would otherwise be unsafe (#356) --- .../huskhomes/BukkitPluginTests.java | 8 -- .../huskhomes/command/EditHomeCommand.java | 21 ++--- .../huskhomes/command/EditWarpCommand.java | 8 +- .../command/PrivateHomeListCommand.java | 5 +- .../command/PublicHomeListCommand.java | 4 +- .../command/SavedPositionCommand.java | 39 ++++++-- .../huskhomes/command/WarpListCommand.java | 2 +- .../huskhomes/database/MySqlDatabase.java | 4 +- .../huskhomes/database/SqLiteDatabase.java | 4 +- .../huskhomes/hook/BlueMapHook.java | 2 +- .../william278/huskhomes/hook/DynmapHook.java | 2 +- .../huskhomes/manager/HomesManager.java | 5 +- .../william278/huskhomes/position/Home.java | 14 +++ .../huskhomes/position/SavedPosition.java | 19 +++- .../william278/huskhomes/util/Validator.java | 3 +- common/src/main/resources/locales/bg-bg.yml | 2 +- common/src/main/resources/locales/de-de.yml | 2 +- common/src/main/resources/locales/en-gb.yml | 2 +- common/src/main/resources/locales/es-es.yml | 2 +- common/src/main/resources/locales/it-it.yml | 2 +- common/src/main/resources/locales/pl-pl.yml | 2 +- common/src/main/resources/locales/tr-tr.yml | 2 +- common/src/main/resources/locales/uk-ua.yml | 2 +- common/src/main/resources/locales/zh-cn.yml | 2 +- common/src/main/resources/locales/zh-tw.yml | 2 +- .../position/SavedPositionTests.java | 88 +++++++++++++++++++ 26 files changed, 188 insertions(+), 60 deletions(-) create mode 100644 common/src/test/java/net/william278/huskhomes/position/SavedPositionTests.java diff --git a/bukkit/src/test/java/net/william278/huskhomes/BukkitPluginTests.java b/bukkit/src/test/java/net/william278/huskhomes/BukkitPluginTests.java index 03f0fe815..e21d3622e 100644 --- a/bukkit/src/test/java/net/william278/huskhomes/BukkitPluginTests.java +++ b/bukkit/src/test/java/net/william278/huskhomes/BukkitPluginTests.java @@ -379,7 +379,6 @@ public void testHomeCreation(@NotNull OnlineUser owner, @NotNull String name, @N .anyMatch(home -> home.equals(name))); } - // rename @DisplayName("Test Home Renaming") @ParameterizedTest(name = "Rename: \"{1}\" > \"{1}2\"") @MethodSource("provideHomeData") @@ -407,7 +406,6 @@ public void testHomeRenaming(@NotNull OnlineUser owner, @NotNull String name, @S .anyMatch(home -> home.equals(name))); } - // description @DisplayName("Test Home Description") @ParameterizedTest(name = "Edit Description: \"{1}\"") @MethodSource("provideHomeData") @@ -424,7 +422,6 @@ public void testHomeDescription(@NotNull OnlineUser owner, @NotNull String name, Assertions.assertEquals(description, homeDescription.get()); } - // relocate @DisplayName("Test Home Relocation") @ParameterizedTest(name = "Relocate: \"{1}\"") @MethodSource("provideHomeData") @@ -443,7 +440,6 @@ public void testHomeRelocation(@NotNull OnlineUser owner, @NotNull String name, Assertions.assertEquals(newPosition.getZ(), homePosition.get().getZ()); } - // overwrite @DisplayName("Test Home Overwrite") @ParameterizedTest(name = "Overwrite: \"{1}\"") @MethodSource("provideHomeData") @@ -462,7 +458,6 @@ public void testHomeOverwrite(@NotNull OnlineUser owner, @NotNull String name, @ Assertions.assertEquals(newPosition.getZ(), homePosition.get().getZ()); } - // make public @DisplayName("Test Making Home Public") @ParameterizedTest(name = "Make Public: \"{1}\"") @MethodSource("provideHomeData") @@ -477,7 +472,6 @@ public void testHomeMakePublic(@NotNull OnlineUser owner, @NotNull String name, Assertions.assertTrue(plugin.getManager().homes().getPublicHomes().get(owner.getUsername()).contains(name)); } - // make private @DisplayName("Test Making Home Private") @ParameterizedTest(name = "Make Private: \"{1}\"") @MethodSource("provideHomeData") @@ -494,7 +488,6 @@ public void testHomeMakePrivate(@NotNull OnlineUser owner, @NotNull String name, .contains(name)); } - // delete @DisplayName("Test Home Deletion") @ParameterizedTest(name = "Delete: \"{1}\"") @MethodSource("provideHomeData") @@ -509,7 +502,6 @@ public void testHomeDeletion(@NotNull OnlineUser owner, @NotNull String name, @S plugin.getManager().homes().createHome(owner, name, position); } - // delete all @DisplayName("Test Deleting All Homes") @Order(9) @Test diff --git a/common/src/main/java/net/william278/huskhomes/command/EditHomeCommand.java b/common/src/main/java/net/william278/huskhomes/command/EditHomeCommand.java index b9ac011ef..a929c56a1 100644 --- a/common/src/main/java/net/william278/huskhomes/command/EditHomeCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/EditHomeCommand.java @@ -21,7 +21,6 @@ import de.themoep.minedown.adventure.MineDown; import net.william278.huskhomes.HuskHomes; -import net.william278.huskhomes.config.Locales; import net.william278.huskhomes.hook.EconomyHook; import net.william278.huskhomes.position.Home; import net.william278.huskhomes.user.CommandUser; @@ -198,11 +197,11 @@ private void setHomePrivacy(@NotNull CommandUser executor, @NotNull Home home, b final String privacy = home.isPublic() ? "public" : "private"; if (ownerEditing) { plugin.getLocales().getLocale("edit_home_privacy_" + privacy + "_success", - home.getMeta().getName()) + home.getName()) .ifPresent(executor::sendMessage); } else { plugin.getLocales().getLocale("edit_home_privacy_" + privacy + "_success_other", - home.getOwner().getUsername(), home.getMeta().getName()) + home.getOwner().getUsername(), home.getName()) .ifPresent(executor::sendMessage); } }); @@ -222,10 +221,10 @@ private List getHomeEditorWindow(@NotNull Home home, boolean otherView boolean showTeleportButton, boolean showPrivacyToggleButton) { return new ArrayList<>() {{ if (!otherViewer) { - plugin.getLocales().getLocale("edit_home_menu_title", home.getMeta().getName()) + plugin.getLocales().getLocale("edit_home_menu_title", home.getName()) .ifPresent(this::add); } else { - plugin.getLocales().getLocale("edit_home_menu_title_other", home.getOwner().getUsername(), home.getMeta().getName()) + plugin.getLocales().getLocale("edit_home_menu_title_other", home.getOwner().getUsername(), home.getName()) .ifPresent(this::add); } @@ -256,21 +255,17 @@ private List getHomeEditorWindow(@NotNull Home home, boolean otherView String.format("%.2f", home.getYaw()), String.format("%.2f", home.getPitch())) .ifPresent(this::add); - final String formattedName = home.getOwner().getUsername() + "." + home.getMeta().getName(); if (showTeleportButton) { - plugin.getLocales().getLocale("edit_home_menu_use_buttons", - formattedName) + plugin.getLocales().getLocale("edit_home_menu_use_buttons", home.getSafeIdentifier()) .ifPresent(this::add); } - final String escapedName = Locales.escapeText(formattedName); - plugin.getLocales().getRawLocale("edit_home_menu_manage_buttons", escapedName, + plugin.getLocales().getRawLocale("edit_home_menu_manage_buttons", home.getSafeIdentifier(), showPrivacyToggleButton ? plugin.getLocales() .getRawLocale("edit_home_menu_privacy_button_" - + (home.isPublic() ? "private" : "public"), escapedName) + + (home.isPublic() ? "private" : "public"), home.getSafeIdentifier()) .orElse("") : "") .map(MineDown::new).ifPresent(this::add); - plugin.getLocales().getLocale("edit_home_menu_meta_edit_buttons", - formattedName) + plugin.getLocales().getLocale("edit_home_menu_meta_edit_buttons", home.getSafeIdentifier()) .ifPresent(this::add); }}; } diff --git a/common/src/main/java/net/william278/huskhomes/command/EditWarpCommand.java b/common/src/main/java/net/william278/huskhomes/command/EditWarpCommand.java index 041b273f2..7db0ee3c4 100644 --- a/common/src/main/java/net/william278/huskhomes/command/EditWarpCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/EditWarpCommand.java @@ -141,7 +141,7 @@ private void setWarpPosition(@NotNull CommandUser executor, @NotNull Warp warp) @NotNull private List getWarpEditorWindow(@NotNull Warp warp) { return new ArrayList<>() {{ - plugin.getLocales().getLocale("edit_warp_menu_title", warp.getMeta().getName()) + plugin.getLocales().getLocale("edit_warp_menu_title", warp.getName()) .ifPresent(this::add); plugin.getLocales().getLocale("edit_warp_menu_metadata", @@ -169,11 +169,11 @@ private List getWarpEditorWindow(@NotNull Warp warp) { String.format("%.2f", warp.getYaw()), String.format("%.2f", warp.getPitch())) .ifPresent(this::add); - plugin.getLocales().getLocale("edit_warp_menu_use_buttons", warp.getMeta().getName()) + plugin.getLocales().getLocale("edit_warp_menu_use_buttons", warp.getSafeIdentifier()) .ifPresent(this::add); - plugin.getLocales().getLocale("edit_warp_menu_manage_buttons", warp.getMeta().getName()) + plugin.getLocales().getLocale("edit_warp_menu_manage_buttons", warp.getSafeIdentifier()) .ifPresent(this::add); - plugin.getLocales().getLocale("edit_warp_menu_meta_edit_buttons", warp.getMeta().getName()) + plugin.getLocales().getLocale("edit_warp_menu_meta_edit_buttons", warp.getSafeIdentifier()) .ifPresent(this::add); }}; } diff --git a/common/src/main/java/net/william278/huskhomes/command/PrivateHomeListCommand.java b/common/src/main/java/net/william278/huskhomes/command/PrivateHomeListCommand.java index 6a5d0458e..553643053 100644 --- a/common/src/main/java/net/william278/huskhomes/command/PrivateHomeListCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/PrivateHomeListCommand.java @@ -108,10 +108,9 @@ private Optional generateList(@NotNull CommandUser executor, @Not final PaginatedList homeList = PaginatedList.of(homes.stream().map(home -> plugin.getLocales() .getRawLocale("home_list_item", - Locales.escapeText(home.getMeta().getName()), - home.getOwner().getUsername() + "." + Locales.escapeText(home.getMeta().getName()), + Locales.escapeText(home.getName()), home.getSafeIdentifier(), Locales.escapeText(plugin.getLocales().wrapText(home.getMeta().getDescription(), 40))) - .orElse(home.getMeta().getName())).sorted().collect(Collectors.toList()), + .orElse(home.getName())).sorted().collect(Collectors.toList()), plugin.getLocales() .getBaseList(plugin.getSettings().getListItemsPerPage()) .setHeaderFormat(plugin.getLocales().getRawLocale("home_list_page_title", diff --git a/common/src/main/java/net/william278/huskhomes/command/PublicHomeListCommand.java b/common/src/main/java/net/william278/huskhomes/command/PublicHomeListCommand.java index 738a1e95f..dc2e232a6 100644 --- a/common/src/main/java/net/william278/huskhomes/command/PublicHomeListCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/PublicHomeListCommand.java @@ -33,7 +33,6 @@ public class PublicHomeListCommand extends ListCommand { - protected PublicHomeListCommand(@NotNull HuskHomes plugin) { super("phomelist", List.of("phomes", "publichomelist"), "[page]", plugin); } @@ -70,8 +69,7 @@ private Optional generateList(@NotNull CommandUser executor, @Not final PaginatedList homeList = PaginatedList.of(publicHomes.stream().map(home -> plugin.getLocales() .getRawLocale("public_home_list_item", - Locales.escapeText(home.getMeta().getName()), - home.getOwner().getUsername() + "." + Locales.escapeText(home.getMeta().getName()), + Locales.escapeText(home.getName()), home.getSafeIdentifier(), Locales.escapeText(home.getOwner().getUsername()), Locales.escapeText(plugin.getLocales().wrapText(home.getMeta().getDescription(), 40))) .orElse(home.getName())).sorted().collect(Collectors.toList()), diff --git a/common/src/main/java/net/william278/huskhomes/command/SavedPositionCommand.java b/common/src/main/java/net/william278/huskhomes/command/SavedPositionCommand.java index 23cc8b441..01301411d 100644 --- a/common/src/main/java/net/william278/huskhomes/command/SavedPositionCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/SavedPositionCommand.java @@ -29,11 +29,13 @@ import net.william278.huskhomes.teleport.TeleportationException; import net.william278.huskhomes.user.CommandUser; import net.william278.huskhomes.user.OnlineUser; +import net.william278.huskhomes.user.User; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; public abstract class SavedPositionCommand extends Command implements TabProvider { @@ -73,9 +75,9 @@ public void execute(@NotNull CommandUser executor, @NotNull String[] args) { public abstract void execute(@NotNull CommandUser executor, @NotNull T position, @NotNull String[] arguments); private Optional resolveHome(@NotNull CommandUser executor, @NotNull String homeName) { - if (homeName.contains(".")) { - final String ownerUsername = homeName.substring(0, homeName.indexOf(".")); - final String ownerHomeName = homeName.substring(homeName.indexOf(".") + 1); + if (homeName.contains(Home.IDENTIFIER_DELIMITER)) { + final String ownerUsername = homeName.substring(0, homeName.indexOf(Home.IDENTIFIER_DELIMITER)); + final String ownerHomeName = homeName.substring(homeName.indexOf(Home.IDENTIFIER_DELIMITER) + 1); if (ownerUsername.isBlank() || ownerHomeName.isBlank()) { plugin.getLocales().getLocale("error_invalid_syntax", getUsage()) .ifPresent(executor::sendMessage); @@ -83,7 +85,7 @@ private Optional resolveHome(@NotNull CommandUser executor, @NotNull Strin } final Optional optionalHome = plugin.getDatabase().getUserDataByName(ownerUsername) - .flatMap(owner -> plugin.getDatabase().getHome(owner.getUser(), ownerHomeName)); + .flatMap(owner -> resolveHomeByName(owner.getUser(), ownerHomeName)); if (optionalHome.isEmpty()) { plugin.getLocales().getLocale(executor.hasPermission(getOtherPermission()) ? "error_home_invalid_other" : "error_public_home_invalid", ownerUsername, ownerHomeName) @@ -101,7 +103,7 @@ private Optional resolveHome(@NotNull CommandUser executor, @NotNull Strin return optionalHome; } else if (executor instanceof OnlineUser owner) { - final Optional optionalHome = plugin.getDatabase().getHome(owner, homeName); + final Optional optionalHome = resolveHomeByName(owner, homeName); if (optionalHome.isEmpty()) { plugin.getLocales().getLocale("error_home_invalid", homeName) .ifPresent(executor::sendMessage); @@ -115,8 +117,20 @@ private Optional resolveHome(@NotNull CommandUser executor, @NotNull Strin } } + private Optional resolveHomeByName(@NotNull User owner, @NotNull String homeName) { + return plugin.getDatabase() + .getHome(owner, homeName) + .or(() -> { + try { + return plugin.getDatabase().getHome(UUID.fromString(homeName)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + }); + } + private Optional resolveWarp(@NotNull CommandUser executor, @NotNull String warpName) { - final Optional warp = plugin.getDatabase().getWarp(warpName); + final Optional warp = resolveWarpByName(warpName); if (warp.isPresent() && executor instanceof OnlineUser user && plugin.getSettings().doPermissionRestrictWarps() && (!user.hasPermission(Warp.getWildcardPermission()) && !user.hasPermission(Warp.getPermission(warpName)))) { plugin.getLocales().getLocale("error_warp_invalid", warpName) @@ -126,6 +140,17 @@ private Optional resolveWarp(@NotNull CommandUser executor, @NotNull Strin return warp; } + private Optional resolveWarpByName(@NotNull String warpName) { + return plugin.getDatabase().getWarp(warpName) + .or(() -> { + try { + return plugin.getDatabase().getWarp(UUID.fromString(warpName)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + }); + } + protected void teleport(@NotNull CommandUser executor, @NotNull Teleportable teleporter, @NotNull T position) { if (!teleporter.equals(executor) && !executor.hasPermission(getPermission("other"))) { plugin.getLocales().getLocale("error_no_permission") @@ -153,7 +178,7 @@ public List suggest(@NotNull CommandUser executor, @NotNull String[] arg if (positionType == Home.class) { return switch (args.length) { case 0, 1 -> { - if (args.length == 1 && args[0].contains(".")) { + if (args.length == 1 && args[0].contains(Home.IDENTIFIER_DELIMITER)) { if (executor.hasPermission(getOtherPermission())) { yield filter(plugin.getManager().homes().getUserHomeNames(), args); } diff --git a/common/src/main/java/net/william278/huskhomes/command/WarpListCommand.java b/common/src/main/java/net/william278/huskhomes/command/WarpListCommand.java index 4248a569a..21cd937ed 100644 --- a/common/src/main/java/net/william278/huskhomes/command/WarpListCommand.java +++ b/common/src/main/java/net/william278/huskhomes/command/WarpListCommand.java @@ -69,7 +69,7 @@ private Optional generateList(@NotNull CommandUser executor, @Not final PaginatedList warpList = PaginatedList.of(warps.stream().map(warp -> plugin.getLocales() .getRawLocale("warp_list_item", - Locales.escapeText(warp.getName()), + Locales.escapeText(warp.getName()), warp.getSafeIdentifier(), Locales.escapeText(plugin.getLocales().wrapText(warp.getMeta().getDescription(), 40))) .orElse(warp.getName())).sorted().collect(Collectors.toList()), plugin.getLocales() diff --git a/common/src/main/java/net/william278/huskhomes/database/MySqlDatabase.java b/common/src/main/java/net/william278/huskhomes/database/MySqlDatabase.java index c4825c3b0..422f091d8 100644 --- a/common/src/main/java/net/william278/huskhomes/database/MySqlDatabase.java +++ b/common/src/main/java/net/william278/huskhomes/database/MySqlDatabase.java @@ -172,7 +172,7 @@ protected int setSavedPosition(@NotNull SavedPosition position, @NotNull Connect Statement.RETURN_GENERATED_KEYS)) { statement.setInt(1, setPosition(position, connection)); - statement.setString(2, position.getMeta().getName()); + statement.setString(2, position.getName()); statement.setString(3, position.getMeta().getDescription()); statement.setString(4, position.getMeta().getSerializedTags()); statement.setTimestamp(5, Timestamp.from(position.getMeta().getCreationTime())); @@ -205,7 +205,7 @@ protected void updateSavedPosition(int savedPositionId, @NotNull SavedPosition p `description`=?, `tags`=? WHERE `id`=?;"""))) { - updateStatement.setString(1, position.getMeta().getName()); + updateStatement.setString(1, position.getName()); updateStatement.setString(2, position.getMeta().getDescription()); updateStatement.setString(3, position.getMeta().getSerializedTags()); updateStatement.setInt(4, savedPositionId); diff --git a/common/src/main/java/net/william278/huskhomes/database/SqLiteDatabase.java b/common/src/main/java/net/william278/huskhomes/database/SqLiteDatabase.java index 9642483e2..175ee9f79 100644 --- a/common/src/main/java/net/william278/huskhomes/database/SqLiteDatabase.java +++ b/common/src/main/java/net/william278/huskhomes/database/SqLiteDatabase.java @@ -181,7 +181,7 @@ protected int setSavedPosition(@NotNull SavedPosition position, @NotNull Connect Statement.RETURN_GENERATED_KEYS)) { statement.setInt(1, setPosition(position, connection)); - statement.setString(2, position.getMeta().getName()); + statement.setString(2, position.getName()); statement.setString(3, position.getMeta().getDescription()); statement.setString(4, position.getMeta().getSerializedTags()); statement.setTimestamp(5, Timestamp.from(position.getMeta().getCreationTime())); @@ -216,7 +216,7 @@ protected void updateSavedPosition(int savedPositionId, @NotNull SavedPosition p `description`=?, `tags`=? WHERE `id`=?;"""))) { - updateStatement.setString(1, position.getMeta().getName()); + updateStatement.setString(1, position.getName()); updateStatement.setString(2, position.getMeta().getDescription()); updateStatement.setString(3, position.getMeta().getSerializedTags()); updateStatement.setInt(4, savedPositionId); diff --git a/common/src/main/java/net/william278/huskhomes/hook/BlueMapHook.java b/common/src/main/java/net/william278/huskhomes/hook/BlueMapHook.java index 78345e99b..707be930d 100644 --- a/common/src/main/java/net/william278/huskhomes/hook/BlueMapHook.java +++ b/common/src/main/java/net/william278/huskhomes/hook/BlueMapHook.java @@ -90,7 +90,7 @@ public void updateHome(@NotNull Home home) { final String markerId = home.getOwner().getUuid() + ":" + home.getUuid(); markerSet.remove(markerId); markerSet.put(markerId, POIMarker.builder() - .label("/phome " + home.getOwner().getUsername() + "." + home.getName()) + .label("/phome " + home.getIdentifier()) .position(home.getX(), home.getY(), home.getZ()) .maxDistance(5000) .icon(getIcon(PUBLIC_HOME_MARKER_IMAGE_NAME), 25, 25) diff --git a/common/src/main/java/net/william278/huskhomes/hook/DynmapHook.java b/common/src/main/java/net/william278/huskhomes/hook/DynmapHook.java index df8f5d5bd..2f817ee6d 100644 --- a/common/src/main/java/net/william278/huskhomes/hook/DynmapHook.java +++ b/common/src/main/java/net/william278/huskhomes/hook/DynmapHook.java @@ -94,7 +94,7 @@ public void updateHome(@NotNull Home home) { .thumbnail(PUBLIC_HOME_MARKER_IMAGE_NAME) .field("Owner", home.getOwner().getUsername()) .field("Description", plugin.getLocales().wrapText(home.getMeta().getDescription(), 60)) - .field("Command", "/phome " + home.getOwner().getUsername() + "." + home.getName()) + .field("Command", "/phome " + home.getIdentifier()) .toHtml()); }); }); diff --git a/common/src/main/java/net/william278/huskhomes/manager/HomesManager.java b/common/src/main/java/net/william278/huskhomes/manager/HomesManager.java index 34df43aaa..103fa4b5b 100644 --- a/common/src/main/java/net/william278/huskhomes/manager/HomesManager.java +++ b/common/src/main/java/net/william278/huskhomes/manager/HomesManager.java @@ -65,8 +65,7 @@ public Map> getUserHomes() { @NotNull public List getUserHomeNames() { return userHomes.entrySet().stream() - .flatMap(e -> e.getValue().stream() - .map(home -> home.getOwner().getUsername() + "." + home.getName())) + .flatMap(e -> e.getValue().stream().map(Home::getIdentifier)) .toList(); } @@ -83,7 +82,7 @@ public Map> getPublicHomes() { @NotNull public List getPublicHomeNames() { return publicHomes.stream() - .map(home -> home.getOwner().getUsername() + "." + home.getName()) + .map(Home::getIdentifier) .toList(); } diff --git a/common/src/main/java/net/william278/huskhomes/position/Home.java b/common/src/main/java/net/william278/huskhomes/position/Home.java index bb8453fb0..cb8a47be2 100644 --- a/common/src/main/java/net/william278/huskhomes/position/Home.java +++ b/common/src/main/java/net/william278/huskhomes/position/Home.java @@ -29,6 +29,7 @@ */ public class Home extends SavedPosition { + public static final String IDENTIFIER_DELIMITER = "."; private final User owner; private boolean isPublic; @@ -74,4 +75,17 @@ public boolean isPublic() { public void setPublic(boolean aPublic) { isPublic = aPublic; } + + @NotNull + @Override + public String getSafeIdentifier() { + return getOwner().getUsername() + IDENTIFIER_DELIMITER + super.getSafeIdentifier(); + } + + @NotNull + @Override + public String getIdentifier() { + return getOwner().getUsername() + IDENTIFIER_DELIMITER + super.getIdentifier(); + } + } diff --git a/common/src/main/java/net/william278/huskhomes/position/SavedPosition.java b/common/src/main/java/net/william278/huskhomes/position/SavedPosition.java index ef0567be2..19bd6d120 100644 --- a/common/src/main/java/net/william278/huskhomes/position/SavedPosition.java +++ b/common/src/main/java/net/william278/huskhomes/position/SavedPosition.java @@ -19,6 +19,7 @@ package net.william278.huskhomes.position; +import net.william278.huskhomes.config.Locales; import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -79,10 +80,26 @@ public UUID getUuid() { return uuid; } + /** + * Get the identifier for this position. This is the name, unless the name contains + * characters that are not allowed in a command argument, in which case it is the UUID. + * + * @return The identifier for this position + */ + @NotNull + public String getSafeIdentifier() { + return Locales.escapeText(getName()).equals(getName()) ? getName() : getUuid().toString(); + } + + @NotNull + public String getIdentifier() { + return getName(); + } + // Compare based on names for alphabetical sorting @Override public int compareTo(@NotNull SavedPosition o) { - return this.getMeta().getName().compareTo(o.getMeta().getName()); + return this.getName().compareTo(o.getName()); } @Override diff --git a/common/src/main/java/net/william278/huskhomes/util/Validator.java b/common/src/main/java/net/william278/huskhomes/util/Validator.java index 17163cdd4..97a6be410 100644 --- a/common/src/main/java/net/william278/huskhomes/util/Validator.java +++ b/common/src/main/java/net/william278/huskhomes/util/Validator.java @@ -20,6 +20,7 @@ package net.william278.huskhomes.util; import net.william278.huskhomes.HuskHomes; +import net.william278.huskhomes.position.Home; import org.jetbrains.annotations.NotNull; @SuppressWarnings("BooleanMethodIsAlwaysInverted") @@ -43,7 +44,7 @@ public Validator(@NotNull HuskHomes plugin) { */ public boolean isValidName(@NotNull String name) { return (isAsciiOnly(name) || plugin.getSettings().doAllowUnicodeNames()) - && !containsWhitespace(name) && !name.contains(".") + && !containsWhitespace(name) && !name.contains(Home.IDENTIFIER_DELIMITER) && name.length() <= MAX_NAME_LENGTH && name.length() >= MIN_NAME_LENGTH; } diff --git a/common/src/main/resources/locales/bg-bg.yml b/common/src/main/resources/locales/bg-bg.yml index a796958dd..1109b5ae4 100644 --- a/common/src/main/resources/locales/bg-bg.yml +++ b/common/src/main/resources/locales/bg-bg.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a bo command_list_title: '[HuskHomes](#00fb9a bold) [| Command List](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Click to suggest command suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/de-de.yml b/common/src/main/resources/locales/de-de.yml index f07940f08..abfc8e7e5 100644 --- a/common/src/main/resources/locales/de-de.yml +++ b/common/src/main/resources/locales/de-de.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% von](#00fb9a) [%3%](#00fb9a b command_list_title: '[HuskHomes](#00fb9a bold) [| Befehlsliste](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Klicke, um den Befehl im Chat einzufügen command suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Seite](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 7506494ce..8184e2721 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a bo command_list_title: '[HuskHomes](#00fb9a bold) [| Command List](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Click to suggest command suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/es-es.yml b/common/src/main/resources/locales/es-es.yml index 8e67bd6a9..d79f4c8d4 100644 --- a/common/src/main/resources/locales/es-es.yml +++ b/common/src/main/resources/locales/es-es.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a bo command_list_title: '[GreenShield](#green bold) [| lista de comandos](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Clic para sugerir el comando suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Página](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/it-it.yml b/common/src/main/resources/locales/it-it.yml index 6a65a3095..783b8d08f 100644 --- a/common/src/main/resources/locales/it-it.yml +++ b/common/src/main/resources/locales/it-it.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Lista Warp:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb command_list_title: '[HuskHomes](#00fb9a bold) [| Lista comandi](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Clicca per usare il comando suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Pagina](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/pl-pl.yml b/common/src/main/resources/locales/pl-pl.yml index 0a1f49259..795966b78 100644 --- a/common/src/main/resources/locales/pl-pl.yml +++ b/common/src/main/resources/locales/pl-pl.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a bo command_list_title: '[HuskHomes](#00fb9a bold) [| Command List](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Click to suggest command suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/tr-tr.yml b/common/src/main/resources/locales/tr-tr.yml index 14a75d9d5..777b47a02 100644 --- a/common/src/main/resources/locales/tr-tr.yml +++ b/common/src/main/resources/locales/tr-tr.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warplar:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a command_list_title: '[HuskHomes](#00fb9a bold) [| Komut Listesi](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Komut önermek için tıklayın suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Sayfa](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/uk-ua.yml b/common/src/main/resources/locales/uk-ua.yml index c2cc52dfb..5c619f1fb 100644 --- a/common/src/main/resources/locales/uk-ua.yml +++ b/common/src/main/resources/locales/uk-ua.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[Warps:](#00fb9a) [(%1%-%2% of](#00fb9a) [%3%](#00fb9a bo command_list_title: '[HuskHomes](#00fb9a bold) [| Command List](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7Click to suggest command suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index 3b8bbebec..47f40ca74 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[地标:](#00fb9a) [(%1%-%2% 共](#00fb9a) [%3%](#00fb9a command_list_title: '[HuskHomes](#00fb9a bold) [| 指令列表](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7点击使用命令 suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/main/resources/locales/zh-tw.yml b/common/src/main/resources/locales/zh-tw.yml index b50dbf687..5bf0f9c58 100644 --- a/common/src/main/resources/locales/zh-tw.yml +++ b/common/src/main/resources/locales/zh-tw.yml @@ -65,7 +65,7 @@ warp_list_page_title: '[地標:](#00fb9a) [(%1%-%2% 共](#00fb9a) [%3%](#00fb9a command_list_title: '[HuskHomes](#00fb9a bold) [| 指令列表](#00fb9a)\n' home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:edithome %2%)' public_home_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7☻ %3%\n&7ℹ %4% run_command=/huskhomes:phome %2%)' -warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %2% run_command=/huskhomes:warp %1%)' +warp_list_item: '[[%1%]](show_text=�fb9a&%1%\n&7ℹ %3% run_command=/huskhomes:warp %2%)' command_list_item: '[/%1%](#00fb9a show_text=&7點擊使用指令 suggest_command=/%1%) [%2%](gray show_text=�fb9a&/%1%\n&7%3%)' list_item_divider: ' [•](gray) ' list_footer: '\n%1%[頁面](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' diff --git a/common/src/test/java/net/william278/huskhomes/position/SavedPositionTests.java b/common/src/test/java/net/william278/huskhomes/position/SavedPositionTests.java new file mode 100644 index 000000000..94535fda3 --- /dev/null +++ b/common/src/test/java/net/william278/huskhomes/position/SavedPositionTests.java @@ -0,0 +1,88 @@ +package net.william278.huskhomes.position; + +import net.william278.huskhomes.user.User; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + +@DisplayName("Saved Position Tests") +public class SavedPositionTests { + + // Map of positions - true if the position contains unsafe characters, false if not + private static final Map POSITION_UNSAFE_NAMES = Map.of( + "TestPosition", false, + "ExamplePos", false, + "SafeName", false, + "[Unsafe]", true, + "Unsafe]", true, + "[Name](bad)", true, + "Unsafe[Name]", true, + "不安全(不安全)", true + ); + + @DisplayName("Test Home Identifiers") + @ParameterizedTest(name = "Home: \"{1}\" (Unsafe: {2})") + @MethodSource("provideHomeData") + public void testHomeIdentifiers(@NotNull Home home, @NotNull @SuppressWarnings("unused") String name, boolean isUnsafeName) { + Assertions.assertEquals( + home.getIdentifier(), + home.getOwner().getUsername() + Home.IDENTIFIER_DELIMITER + home.getName() + ); + Assertions.assertEquals( + home.getSafeIdentifier(), + isUnsafeName + ? home.getOwner().getUsername() + Home.IDENTIFIER_DELIMITER + home.getUuid() + : home.getIdentifier() + ); + } + + @DisplayName("Test Warp Identifiers") + @ParameterizedTest(name = "Warp: \"{1}\" (Unsafe: {2})") + @MethodSource("provideWarpData") + public void testWarpIdentifiers(@NotNull Warp warp, @NotNull @SuppressWarnings("unused") String name, boolean isUnsafeName) { + Assertions.assertEquals( + warp.getIdentifier(), + warp.getName() + ); + Assertions.assertEquals( + warp.getSafeIdentifier(), + isUnsafeName ? warp.getUuid().toString() : warp.getIdentifier() + ); + } + + @NotNull + private static Stream provideWarpData() { + final Position position = Position.at(63.25, 127.43, -32, 180f, -94.3f, + World.from("TestWorld", UUID.randomUUID()), "TestServer"); + return POSITION_UNSAFE_NAMES.entrySet().stream() + .map(entry -> Arguments.of( + Warp.from(position, PositionMeta.create(entry.getKey(), "")), + entry.getKey(), + entry.getValue() + )); + } + + @NotNull + private static Stream provideHomeData() { + final Position position = Position.at(63.25, 127.43, -32, 180f, -94.3f, + World.from("TestWorld", UUID.randomUUID()), "TestServer"); + return POSITION_UNSAFE_NAMES.entrySet().stream() + .map(entry -> Arguments.of( + Home.from( + position, + PositionMeta.create(entry.getKey(), ""), + User.of(UUID.randomUUID(), "TestUser") + ), + entry.getKey(), + entry.getValue())); + } + + +}