diff --git a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java index acade7945..2fed28d46 100644 --- a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java +++ b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java @@ -270,7 +270,7 @@ private Collection suggestGeneratorPlugins(BukkitCommandCompletionContex private Collection suggestMVConfigValues(BukkitCommandCompletionContext context) { return Try.of(() -> context.getContextValue(String.class)) .map(propertyName -> config.getStringPropertyHandle() - .getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET)) + .getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET, context.getSender())) .getOrElse(Collections.emptyList()); } @@ -316,7 +316,7 @@ private Collection suggestMVWorldPropsValue(BukkitCommandCompletionConte MultiverseWorld world = context.getContextValue(MultiverseWorldValue.class).value(); PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class); String propertyName = context.getContextValue(String.class); - return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action); + return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender()); }).getOrElse(Collections.emptyList()); } @@ -357,7 +357,7 @@ private Collection suggestSpawnCategoryPropsValue(BukkitCommandCompletio return world.getEntitySpawnConfig() .getSpawnCategoryConfig(spawnCategory) .getStringPropertyHandle() - .getSuggestedPropertyValue(propertyName, context.getInput(), action); + .getSuggestedPropertyValue(propertyName, context.getInput(), action, context.getSender()); }).getOrElse(Collections.emptyList()); } } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java index a28b7e293..d591b1b9b 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java @@ -4,7 +4,7 @@ import io.vavr.control.Try; import jakarta.inject.Inject; import jakarta.inject.Provider; -import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.event.EventPriority; import org.bukkit.plugin.PluginManager; @@ -548,13 +548,12 @@ private N node(N node) { .build()); // todo: Maybe combine with the similar method in MVCommandCompletion but that has permission checking - private Collection suggestDestinations(String input) { - return destinationsProvider.get().getDestinations().stream() - .flatMap(destination -> destination.suggestDestinations(Bukkit.getConsoleSender(), null) - .stream() - .map(packet -> destination instanceof WorldDestination - ? packet.destinationString() - : destination.getIdentifier() + ":" + packet.destinationString())) + private Collection suggestDestinations(CommandSender sender, String input) { + return destinationsProvider.get().suggestDestinations(sender, input) + .stream() + .map(packet -> packet.destination() instanceof WorldDestination + ? packet.destinationString() + : packet.destination().getIdentifier() + ":" + packet.destinationString()) .toList(); } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java b/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java index 95639bd03..7a2e9d95d 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java @@ -5,6 +5,9 @@ import io.vavr.control.Option; import io.vavr.control.Try; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -71,20 +74,47 @@ public Try> getPropertyType(@Nullable String name) { /** * Suggests property values for command auto-complete based on the input and action type. * - * @param name The name of the property. - * @param input The input value to suggest based on. - * @param action The modification action being performed. + * @param name The name of the property. + * @param input The input value to suggest based on. + * @param action The modification action being performed. * @return A collection of suggested values. */ public Collection getSuggestedPropertyValue( - @Nullable String name, @Nullable String input, @NotNull PropertyModifyAction action) { + @Nullable String name, + @Nullable String input, + @NotNull PropertyModifyAction action + ) { + return getSuggestedPropertyValue(name, input, action, Bukkit.getConsoleSender()); + } + + /** + * Suggests property values for command auto-complete based on the input and action type. + *
+ * Providing a sender gives contextual information such as sender name, permissions, or player location + * for better suggestions. + * + * @param name The name of the property. + * @param input The input value to suggest based on. + * @param action The modification action being performed. + * @param sender The sender context to use. + * @return A collection of suggested values + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Collection getSuggestedPropertyValue( + @Nullable String name, + @Nullable String input, + @NotNull PropertyModifyAction action, + @NotNull CommandSender sender + ) { return switch (action) { case SET -> findNode(name, ValueNode.class) - .map(node -> node.suggest(input)) + .map(node -> node.suggest(sender, input)) .getOrElse(Collections.emptyList()); case ADD -> findNode(name, ListValueNode.class) - .map(node -> node.suggestItem(input)) + .map(node -> node.suggestItem(sender, input)) .getOrElse(Collections.emptyList()); case REMOVE -> findNode(name, ListValueNode.class) diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java index 81cdd2d35..e15b1a8b7 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java @@ -8,9 +8,12 @@ import io.vavr.control.Option; import io.vavr.control.Try; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.config.node.functions.SenderNodeSuggester; import org.mvplugins.multiverse.core.config.node.serializer.DefaultSerializerProvider; import org.mvplugins.multiverse.core.config.node.functions.DefaultStringParserProvider; import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider; @@ -115,6 +118,17 @@ protected ConfigNode( return Collections.emptyList(); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggest(@NotNull CommandSender sender, @Nullable String input) { + if (suggester != null && suggester instanceof SenderNodeSuggester senderSuggester) { + return senderSuggester.suggest(sender, input); + } + return suggest(input); + } + /** * {@inheritDoc} */ @@ -254,6 +268,20 @@ protected Builder(@NotNull String path, @NotNull Class type) { return self(); } + /** + * Sets the suggester for this node with sender context. + * + * @param suggester The suggester for this node. + * @return This builder. + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public @NotNull B suggester(@NotNull SenderNodeSuggester suggester) { + this.suggester = suggester; + return self(); + } + /** * Sets the string parser for this node. * diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java index 45ca1fb1a..f306fc41e 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java @@ -5,7 +5,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; @@ -13,6 +12,8 @@ import io.vavr.Value; import io.vavr.control.Try; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -20,6 +21,7 @@ import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider; import org.mvplugins.multiverse.core.config.node.functions.NodeStringParser; import org.mvplugins.multiverse.core.config.node.functions.NodeSuggester; +import org.mvplugins.multiverse.core.config.node.functions.SenderNodeSuggester; import org.mvplugins.multiverse.core.config.node.serializer.DefaultSerializerProvider; import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer; import org.mvplugins.multiverse.core.utils.REPatterns; @@ -110,12 +112,12 @@ private void setDefaults() { } private void setDefaultSuggester() { - this.suggester = input -> { - if (input == null) { - return itemSuggester.suggest(null); - } - return StringFormatter.addonToCommaSeperated(input, itemSuggester.suggest(input)); - }; + if (itemSuggester instanceof SenderNodeSuggester senderItemSuggester) { + this.suggester = (SenderNodeSuggester)(sender, input) -> + StringFormatter.addonToCommaSeperated(input, senderItemSuggester.suggest(sender, input)); + } else { + this.suggester = input -> StringFormatter.addonToCommaSeperated(input, itemSuggester.suggest(input)); + } } private void setDefaultStringParser() { @@ -194,6 +196,17 @@ private void setDefaultOnSetValue() { return Collections.emptyList(); } + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestItem(@NotNull CommandSender sender, @Nullable String input) { + if (itemSuggester != null && itemSuggester instanceof SenderNodeSuggester senderSuggester) { + return senderSuggester.suggest(sender, input); + } + return suggestItem(input); + } + /** * {@inheritDoc} */ @@ -267,6 +280,20 @@ protected Builder(@NotNull String path, @NotNull Class itemType) { return self(); } + /** + * Sets the suggester for an individual item in the list with sender context. + * + * @param itemSuggester The suggester. + * @return This builder. + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public @NotNull B itemSuggester(@NotNull SenderNodeSuggester itemSuggester) { + this.itemSuggester = itemSuggester; + return self(); + } + /** * Sets the string parser for an individual item in the list. * diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ListValueNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ListValueNode.java index 9b9eff851..b540d15c0 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ListValueNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ListValueNode.java @@ -4,6 +4,8 @@ import java.util.List; import io.vavr.control.Try; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,6 +33,19 @@ public interface ListValueNode extends ValueNode> { */ @NotNull Collection suggestItem(@Nullable String input); + /** + * Suggests possible string values for this node. Use contextural information from the sender such as + * sender name, permissions, or player location for better suggestions. + * + * @param sender The sender context. + * @param input The input string. + * @return A collection of possible string values + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + @NotNull Collection suggestItem(@NotNull CommandSender sender, @Nullable String input); + /** * Parses the given string into a value of type {@link I}. Used for property set by user input. * diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java index 51db14a68..418cee043 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java @@ -4,6 +4,8 @@ import io.vavr.control.Option; import io.vavr.control.Try; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,6 +42,19 @@ public interface ValueNode extends Node { */ @NotNull Collection suggest(@Nullable String input); + /** + * Suggests possible string values for this node. Use contextural information from the sender such as + * sender name, permissions, or player location for better suggestions. + * + * @param sender The sender context. + * @param input The input string. + * @return A collection of possible string values + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + @NotNull Collection suggest(@NotNull CommandSender sender, @Nullable String input); + /** * Parses the given string into a value of type {@link T}. Used for property set by user input. * diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeSuggester.java b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeSuggester.java new file mode 100644 index 000000000..19d56d962 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeSuggester.java @@ -0,0 +1,36 @@ +package org.mvplugins.multiverse.core.config.node.functions; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * A function that suggests possible values for a node value with sender contextual information. + * + * @since 5.1 + */ +@ApiStatus.AvailableSince("5.1") +@FunctionalInterface +public interface SenderNodeSuggester extends NodeSuggester { + + /** + * Suggests possible values for a node value. Generated based on the current user input and sender contextual information. + * + * @param sender The sender context. + * @param input The current partial user input + * @return The possible values. + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + @NotNull Collection suggest(@NotNull CommandSender sender, @Nullable String input); + + @Override + default @NotNull Collection suggest(@Nullable String input) { + return suggest(Bukkit.getConsoleSender(), input); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java index 570537493..f04984698 100644 --- a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java @@ -1,14 +1,17 @@ package org.mvplugins.multiverse.core.destination.core; import java.util.Collection; +import java.util.stream.Stream; import co.aikar.locales.MessageKey; import co.aikar.locales.MessageKeyProvider; +import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Option; import jakarta.inject.Inject; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; @@ -17,7 +20,6 @@ import org.mvplugins.multiverse.core.destination.Destination; import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; import org.mvplugins.multiverse.core.locale.MVCorei18n; -import org.mvplugins.multiverse.core.locale.message.MessageReplacement; import org.mvplugins.multiverse.core.utils.REPatterns; import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.FailureReason; @@ -124,13 +126,38 @@ private Option getLoadedMultiverseWorld(String worldName) @Override public @NotNull Collection suggestDestinations( @NotNull CommandSender sender, @Nullable String destinationParams) { - return worldManager.getLoadedWorlds().stream() + Stream stream = worldManager.getLoadedWorlds().stream() .filter(world -> worldEntryCheckerProvider.forSender(sender) .canAccessWorld(world) .isSuccess()) .map(world -> - new DestinationSuggestionPacket(this, world.getTabCompleteName() + ":", world.getName())) - .toList(); + new DestinationSuggestionPacket(this, world.getTabCompleteName() + ":", world.getName())); + if (sender instanceof Player player) { + var playerLocation = new DestinationSuggestionPacket( + this, + "%s:%.2f,%.2f,%.2f".formatted( + player.getWorld().getName(), + player.getLocation().getX(), + player.getLocation().getY(), + player.getLocation().getZ() + ), + player.getWorld().getName() + ); + var playerLocationPW = new DestinationSuggestionPacket( + this, + "%s:%.2f,%.2f,%.2f:%.2f:%.2f".formatted( + player.getWorld().getName(), + player.getLocation().getX(), + player.getLocation().getY(), + player.getLocation().getZ(), + player.getLocation().getPitch(), + player.getLocation().getYaw() + ), + player.getWorld().getName() + ); + stream = Stream.concat(stream, Stream.of(playerLocation, playerLocationPW)); + } + return stream.toList(); } public enum InstanceFailureReason implements FailureReason {