Skip to content

Commit 88a9ee5

Browse files
authored
Merge pull request #3387 from Multiverse/fix/selectors-error-spam
Fix selectors spamming stacktrace and improve invalid format message
2 parents 46f6724 + 89e765b commit 88a9ee5

2 files changed

Lines changed: 66 additions & 30 deletions

File tree

src/main/java/org/mvplugins/multiverse/core/command/MVCommandContexts.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import co.aikar.commands.PaperCommandContexts;
1212
import co.aikar.commands.contexts.ContextResolver;
1313
import com.google.common.base.Strings;
14+
import io.vavr.control.Option;
1415
import io.vavr.control.Try;
1516
import jakarta.inject.Inject;
1617
import org.bukkit.Bukkit;
@@ -272,10 +273,17 @@ private IssuerAwareContextBuilder<Player> playerContextBuilder() {
272273
private IssuerAwareContextBuilder<Player[]> playerArrayContextBuilder() {
273274
return new IssuerAwareContextBuilder<Player[]>()
274275
.fromPlayer((context, player) -> new Player[]{player})
275-
.fromInput((context, input) -> {
276-
Player[] players = PlayerFinder.getMulti(input, context.getSender()).toArray(new Player[0]);
277-
return (players.length == 0) ? null : players;
278-
})
276+
.fromInput((context, input) -> PlayerFinder
277+
.tryGetMulti(input, context.getSender())
278+
.map(list -> list.toArray(new Player[0]))
279+
.map(arr -> (arr.length == 0) ? null : arr)
280+
.getOrElseThrow(failure -> {
281+
if (failure instanceof MVInvalidCommandArgument mvFailure) {
282+
throw mvFailure;
283+
}
284+
throw new InvalidCommandArgument(failure.getLocalizedMessage() + " "
285+
+ Option.of(failure.getCause()).map(Throwable::getLocalizedMessage).getOrElse(""));
286+
}))
279287
.issuerOnlyFailMessage((context) -> Message.of("This command can only be used by a player."))
280288
.issuerAwareInputFailMessage((context, input) -> Message.of("Invalid player: " + input + ". Either specify an online player or use this command as a player."))
281289
.inputOnlyFailMessage((context, input) -> {

src/main/java/org/mvplugins/multiverse/core/utils/PlayerFinder.java

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.mvplugins.multiverse.core.utils;
22

33
import java.util.ArrayList;
4+
import java.util.Collections;
45
import java.util.List;
56
import java.util.UUID;
67
import java.util.stream.Collectors;
78

89
import com.dumptruckman.minecraft.util.Logging;
910
import com.google.common.base.Strings;
11+
import io.vavr.control.Try;
1012
import org.bukkit.Bukkit;
1113
import org.bukkit.command.CommandSender;
1214
import org.bukkit.entity.Player;
@@ -76,22 +78,33 @@ public final class PlayerFinder {
7678
public static @NotNull List<Player> getMulti(@Nullable String playerIdentifiers,
7779
@NotNull CommandSender sender
7880
) {
79-
List<Player> playerResults = new ArrayList<>();
81+
return tryGetMulti(playerIdentifiers, sender)
82+
.getOrElse(Collections.emptyList());
83+
}
8084

85+
/**
86+
* Get multiple {@link Player} based on many identifiers of name UUID or selector.
87+
*
88+
* @param playerIdentifiers An identifier of multiple names, UUIDs or selectors, separated by comma.
89+
* @param sender Target sender for selector.
90+
* @return A list of all the {@link Player} found.
91+
*
92+
* @since 5.4
93+
*/
94+
@ApiStatus.AvailableSince("5.4")
95+
public static @NotNull Try<@NotNull List<Player>> tryGetMulti(@Nullable String playerIdentifiers,
96+
@NotNull CommandSender sender
97+
) {
8198
if (playerIdentifiers == null || Strings.isNullOrEmpty(playerIdentifiers)) {
82-
return playerResults;
99+
return Try.success(Collections.emptyList());
83100
}
84101

85102
// TODO: Currently just assume entire string is a selector. Add support for comma seperated mixture of names, uuids and selectors
86103
if (isSelector(playerIdentifiers)) {
87-
Logging.finer("Using selector: %s", playerIdentifiers);
88-
List<Player> targetPlayers = getMultiBySelector(playerIdentifiers, sender);
89-
if (targetPlayers != null) {
90-
playerResults.addAll(targetPlayers);
91-
}
92-
return playerResults;
104+
return tryGetMultiBySelector(playerIdentifiers, sender);
93105
}
94106

107+
List<Player> playerResults = new ArrayList<>();
95108
String[] playerIdentifierArray = REPatterns.COMMA.split(playerIdentifiers);
96109
for (String playerIdentifier : playerIdentifierArray) {
97110
Player targetPlayer = getByName(playerIdentifier);
@@ -104,12 +117,13 @@ public final class PlayerFinder {
104117
playerResults.add(targetPlayer);
105118
continue;
106119
}
107-
List<Player> targetPlayers = getMultiBySelector(playerIdentifier, sender);
108-
if (targetPlayers != null) {
109-
playerResults.addAll(targetPlayers);
120+
Try<@NotNull List<Player>> selectorParseResult = tryGetMultiBySelector(playerIdentifier, sender);
121+
if (selectorParseResult.isFailure()) {
122+
return Try.failure(selectorParseResult.getCause());
110123
}
124+
playerResults.addAll(selectorParseResult.getOrElse(Collections.emptyList()));
111125
}
112-
return playerResults;
126+
return Try.success(playerResults);
113127
}
114128

115129
/**
@@ -176,7 +190,7 @@ public static Player getByName(@NotNull String playerName) {
176190
@NotNull CommandSender sender
177191
) {
178192
List<Player> matchedPlayers = getMultiBySelector(playerSelector, sender);
179-
if (matchedPlayers == null || matchedPlayers.isEmpty()) {
193+
if (matchedPlayers.isEmpty()) {
180194
Logging.fine("No player found with selector '%s' for %s.", playerSelector, sender.getName());
181195
return null;
182196
}
@@ -195,22 +209,36 @@ public static Player getByName(@NotNull String playerName) {
195209
* @param sender Target sender for selector.
196210
* @return A list of all the {@link Player} found.
197211
*/
198-
public static @Nullable List<Player> getMultiBySelector(@NotNull String playerSelector,
212+
public static @NotNull List<Player> getMultiBySelector(@NotNull String playerSelector,
199213
@NotNull CommandSender sender
214+
) {
215+
return tryGetMultiBySelector(playerSelector, sender)
216+
.onFailure(throwable -> Logging.warning(
217+
"Error selecting entities with selector '%s' for %s: %s",
218+
playerSelector, sender.getName(), throwable.getMessage()
219+
))
220+
.getOrElse(Collections::emptyList);
221+
}
222+
223+
/**
224+
* Get multiple {@link Player} based on <a href="https://minecraft.gamepedia.com/Commands#Target_selectors">vanilla selectors</a>.
225+
*
226+
* @param playerSelector A target selector, usually starts with an '@'.
227+
* @param sender Target sender for selector.
228+
* @return A list of all the {@link Player} found.
229+
*
230+
* @since 5.4
231+
*/
232+
@ApiStatus.AvailableSince("5.4")
233+
public static @NotNull Try<@NotNull List<Player>> tryGetMultiBySelector(@NotNull String playerSelector,
234+
@NotNull CommandSender sender
200235
) {
201236
if (playerSelector.charAt(0) != '@') {
202-
return null;
203-
}
204-
try {
205-
return Bukkit.selectEntities(sender, playerSelector).stream()
206-
.filter(e -> e instanceof Player)
207-
.map(e -> ((Player) e))
208-
.collect(Collectors.toList());
209-
} catch (IllegalArgumentException e) {
210-
Logging.warning("An error occurred while parsing selector '%s' for %s. Is it is the correct format?",
211-
playerSelector, sender.getName());
212-
e.printStackTrace();
213-
return null;
237+
return Try.success(Collections.emptyList());
214238
}
239+
return Try.of(() -> Bukkit.selectEntities(sender, playerSelector).stream()
240+
.filter(e -> e instanceof Player)
241+
.map(e -> ((Player) e))
242+
.collect(Collectors.toList()));
215243
}
216244
}

0 commit comments

Comments
 (0)