Skip to content

Commit 2cfcdad

Browse files
committed
Improve teleport tab complete handling
1 parent 4dc6163 commit 2cfcdad

File tree

6 files changed

+130
-28
lines changed

6 files changed

+130
-28
lines changed

src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ class TeleportCommand extends CoreCommand {
6060
@CommandAlias("mvtp")
6161
@Subcommand("teleport|tp")
6262
@CommandPermission("@mvteleport")
63-
@CommandCompletion("@players|@destinations:playerOnly @destinations:othersOnly|@flags:groupName=mvteleportcommand @flags:groupName=mvteleportcommand")
63+
@CommandCompletion(
64+
"@destinations:playerOnly|@playersarray:excludeSelf,checkPermissions=@mvteleportother " +
65+
"@destinations:othersOnly|@flags:groupName=mvteleportcommand,resolveUntil=arg2 " +
66+
"@flags:groupName=mvteleportcommand"
67+
)
6468
@Syntax("[player] <destination> [--unsafe]")
6569
@Description("{@@mv-core.teleport.description}")
6670
void onTeleportCommand(

src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandCompletions.java

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.mvplugins.multiverse.core.commandtools;
22

3+
import java.util.ArrayList;
34
import java.util.Arrays;
45
import java.util.Collection;
56
import java.util.Collections;
@@ -20,10 +21,13 @@
2021
import com.google.common.collect.Sets;
2122
import io.vavr.control.Try;
2223
import jakarta.inject.Inject;
24+
import org.apache.commons.lang.Validate;
25+
import org.bukkit.Bukkit;
2326
import org.bukkit.Difficulty;
2427
import org.bukkit.GameMode;
2528
import org.bukkit.GameRule;
2629
import org.bukkit.World;
30+
import org.bukkit.command.CommandSender;
2731
import org.bukkit.entity.Player;
2832
import org.jetbrains.annotations.NotNull;
2933
import org.jvnet.hk2.annotations.Service;
@@ -75,6 +79,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
7579
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
7680
registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
7781
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
82+
registerAsyncCompletion("playersarray", this::suggestPlayersArray);
7883
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
7984

8085
setDefaultCompletion("destinations", DestinationInstance.class);
@@ -86,6 +91,39 @@ public class MVCommandCompletions extends PaperCommandCompletions {
8691
setDefaultCompletion("mvworlds", LoadedMultiverseWorld.class);
8792
}
8893

94+
@Override
95+
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
96+
return super.registerCompletion(id, context ->
97+
completeWithPreconditions(context, handler));
98+
}
99+
100+
@Override
101+
public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<BukkitCommandCompletionContext> handler) {
102+
return super.registerAsyncCompletion(id, context ->
103+
completeWithPreconditions(context, handler));
104+
}
105+
106+
private Collection<String> completeWithPreconditions(
107+
BukkitCommandCompletionContext context,
108+
CommandCompletionHandler<BukkitCommandCompletionContext> handler) {
109+
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
110+
return Collections.emptyList();
111+
}
112+
if (context.hasConfig("resolveUntil")) {
113+
if (!Try.run(() -> context.getContextValueByName(Object.class, context.getConfig("resolveUntil"))).isSuccess()) {
114+
return Collections.emptyList();
115+
}
116+
}
117+
if (context.hasConfig("checkPermissions")) {
118+
for (String permission : context.getConfig("checkPermissions").split(";")) {
119+
if (!commandManager.getCommandPermissions().hasPermission(context.getIssuer(), permission)) {
120+
return Collections.emptyList();
121+
}
122+
}
123+
}
124+
return handler.getCompletions(context);
125+
}
126+
89127
/**
90128
* Shortcut to suggest enums values
91129
*
@@ -128,9 +166,6 @@ private boolean checkPerms(CommandIssuer issuer, RegisteredCommand<?> command) {
128166
}
129167

130168
private Collection<String> suggestDestinations(BukkitCommandCompletionContext context) {
131-
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
132-
return Collections.emptyList();
133-
}
134169
return Try.of(() -> context.getContextValue(Player[].class))
135170
.map(players -> {
136171
Player player = Arrays.stream(players)
@@ -141,6 +176,9 @@ private Collection<String> suggestDestinations(BukkitCommandCompletionContext co
141176
// Most likely console did not specify a player
142177
return Collections.<String>emptyList();
143178
}
179+
if (context.hasConfig("othersOnly") && player.equals(context.getPlayer())) {
180+
return Collections.<String>emptyList();
181+
}
144182
return suggestDestinationsWithPerms((BukkitCommandIssuer) context.getIssuer(), player, context.getInput());
145183
})
146184
.getOrElse(Collections.emptyList());
@@ -185,22 +223,10 @@ private Collection<String> suggestMVConfigValues(BukkitCommandCompletionContext
185223
}
186224

187225
private Collection<String> suggestMVWorlds(BukkitCommandCompletionContext context) {
188-
if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) {
189-
return Collections.emptyList();
190-
}
191-
192226
if (!context.hasConfig("multiple")) {
193227
return getMVWorldNames(context);
194228
}
195-
196-
String input = context.getInput();
197-
int lastComma = input.lastIndexOf(',');
198-
String currentWorldsString = input.substring(0, lastComma + 1);
199-
Set<String> currentWorlds = Sets.newHashSet(input.split(","));
200-
return getMVWorldNames(context).stream()
201-
.filter(world -> !currentWorlds.contains(world))
202-
.map(world -> currentWorldsString + world)
203-
.collect(Collectors.toList());
229+
return addonToCommaSeperated(context.getInput(), getMVWorldNames(context));
204230
}
205231

206232
private List<String> getMVWorldNames(BukkitCommandCompletionContext context) {
@@ -248,4 +274,31 @@ private Collection<String> suggestMVWorldPropsValue(BukkitCommandCompletionConte
248274
return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action);
249275
}).getOrElse(Collections.emptyList());
250276
}
277+
278+
279+
private Collection<String> suggestPlayersArray(BukkitCommandCompletionContext context) {
280+
CommandSender sender = context.getSender();
281+
Validate.notNull(sender, "Sender cannot be null");
282+
Player senderPlayer = sender instanceof Player ? (Player)sender : null;
283+
List<String> matchedPlayers = new ArrayList<>();
284+
285+
for(Player player : Bukkit.getOnlinePlayers()) {
286+
String name = player.getName();
287+
if ((senderPlayer == null || senderPlayer.canSee(player))
288+
&& (!context.hasConfig("excludeSelf") || !player.equals(senderPlayer))) {
289+
matchedPlayers.add(name);
290+
}
291+
}
292+
return addonToCommaSeperated(context.getInput(), matchedPlayers);
293+
}
294+
295+
private Collection<String> addonToCommaSeperated(String input, Collection<String> addons) {
296+
int lastComma = input.lastIndexOf(',');
297+
String previousInputs = input.substring(0, lastComma + 1);
298+
Set<String> inputSet = Sets.newHashSet(input.split(","));
299+
return addons.stream()
300+
.filter(suggestion -> !inputSet.contains(suggestion))
301+
.map(suggestion -> previousInputs + suggestion)
302+
.toList();
303+
}
251304
}

src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandPermissions.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020
@Service
2121
public class MVCommandPermissions {
2222
private final Map<String, Predicate<CommandIssuer>> permissionsCheckMap;
23-
private final CorePermissionsChecker permissionsChecker;
2423

2524
@Inject
2625
MVCommandPermissions(@NotNull CorePermissionsChecker permissionsChecker) {
2726
this.permissionsCheckMap = new HashMap<>();
28-
this.permissionsChecker = permissionsChecker;
2927

28+
registerPermissionChecker("mvteleportother", issuer -> permissionsChecker.hasTeleportOtherPermission(issuer.getIssuer()));
3029
registerPermissionChecker("mvteleport", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer()));
3130
registerPermissionChecker("mvspawn", issuer -> permissionsChecker.hasAnySpawnPermission(issuer.getIssuer()));
3231
}

src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissionsChecker.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.mvplugins.multiverse.core.permissions;
22

3-
import com.dumptruckman.minecraft.util.Logging;
43
import jakarta.inject.Inject;
54
import org.bukkit.command.CommandSender;
65
import org.bukkit.entity.Entity;
@@ -144,4 +143,14 @@ public boolean hasAnyTeleportPermission(CommandSender sender) {
144143
}
145144
return false;
146145
}
146+
147+
public boolean hasTeleportOtherPermission(CommandSender sender) {
148+
for (Destination<?, ?> destination : destinationsProvider.getDestinations()) {
149+
String permission = concatPermission(CorePermissions.TELEPORT, "other", destination.getIdentifier());
150+
if (hasPermission(sender, permission)) {
151+
return true;
152+
}
153+
}
154+
return false;
155+
}
147156
}

src/test/java/org/mvplugins/multiverse/core/TestWithMockBukkit.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ abstract class TestWithMockBukkit {
6767
}
6868

6969
fun assertLocationEquals(expected: Location?, actual: Location?) {
70-
assertEquals(expected?.world, actual?.world)
71-
assertEquals(expected?.x, actual?.x)
72-
assertEquals(expected?.y, actual?.y)
73-
assertEquals(expected?.z, actual?.z)
74-
assertEquals(expected?.yaw, actual?.yaw)
75-
assertEquals(expected?.pitch, actual?.pitch)
70+
assertEquals(expected?.world, actual?.world, "Worlds don't match for location comparison ($expected, $actual)")
71+
assertEquals(expected?.x, actual?.x, "X values don't match for location comparison ($expected, $actual)")
72+
assertEquals(expected?.y, actual?.y, "Y values don't match for location comparison ($expected, $actual)")
73+
assertEquals(expected?.z, actual?.z, "Z values don't match for location comparison ($expected, $actual)")
74+
assertEquals(expected?.yaw, actual?.yaw, "Yaw values don't match for location comparison ($expected, $actual)")
75+
assertEquals(expected?.pitch, actual?.pitch, "Pitch values don't match for location comparison ($expected, $actual)")
7676
}
7777
}

src/test/java/org/mvplugins/multiverse/core/commands/TeleportCommandTest.kt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import org.bukkit.Bukkit
44
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
55
import kotlin.test.BeforeTest
66
import kotlin.test.Test
7-
import kotlin.test.assertEquals
87
import kotlin.test.assertTrue
98

109
class TeleportCommandTest : AbstractCommandTest() {
1110

1211
@BeforeTest
1312
fun setUp() {
14-
server.setPlayers(3)
13+
server.setPlayers(4)
1514
assertTrue(worldManager.createWorld(CreateWorldOptions.worldName("otherworld")).isSuccess)
1615
}
1716

@@ -44,4 +43,42 @@ class TeleportCommandTest : AbstractCommandTest() {
4443
Thread.sleep(100) // wait for the player to teleport asynchronously
4544
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
4645
}
46+
47+
@Test
48+
fun `Player permission to teleport self`() {
49+
addPermission("multiverse.teleport.self.w")
50+
addPermission("multiverse.teleport.self.w.otherworld")
51+
52+
player.performCommand("mv tp otherworld --unsafe")
53+
Thread.sleep(100) // wait for the player to teleport asynchronously
54+
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, player.location)
55+
56+
player.performCommand("mv tp Player1 world --unsafe")
57+
Thread.sleep(100) // wait for the player to teleport asynchronously
58+
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player1")?.location)
59+
60+
player.performCommand("mv tp Player2,Player3 world --unsafe")
61+
Thread.sleep(100) // wait for the player to teleport asynchronously
62+
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player2")?.location)
63+
assertLocationEquals(server.getWorld("world")?.spawnLocation, server.getPlayer("Player3")?.location)
64+
}
65+
66+
@Test
67+
fun `Player permission to teleport others`() {
68+
addPermission("multiverse.teleport.other.w")
69+
addPermission("multiverse.teleport.other.w.otherworld")
70+
71+
player.performCommand("mv tp Player1 otherworld --unsafe")
72+
Thread.sleep(100) // wait for the player to teleport asynchronously
73+
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player1")?.location)
74+
75+
player.performCommand("mv tp Player2,Player3 otherworld --unsafe")
76+
Thread.sleep(100) // wait for the player to teleport asynchronously
77+
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player2")?.location)
78+
assertLocationEquals(server.getWorld("otherworld")?.spawnLocation, server.getPlayer("Player3")?.location)
79+
80+
player.performCommand("mv tp otherworld --unsafe")
81+
Thread.sleep(100) // wait for the player to teleport asynchronously
82+
assertLocationEquals(server.getWorld("world")?.spawnLocation, player.location)
83+
}
4784
}

0 commit comments

Comments
 (0)