feat(commands): add contextual tab completion
This commit is contained in:
@@ -6,6 +6,18 @@ description: Chronological record of material decisions affecting the Spigot Tyr
|
||||
|
||||
# Spigot Tyrant Design Log
|
||||
|
||||
## 2026-08-21 — Contextual command completion completed
|
||||
|
||||
- Completed US-018 with registered completers for `/tyrant`, `/vigilante`, and `/tyrantadmin`, replacing Bukkit's unconditional online-player fallback.
|
||||
- Suggestions now follow argument position, filter case-insensitively, expose eligible assignment and recruitment targets, identify current Followers for dismissal, and gate administration by permission.
|
||||
- Administrative completion remains available to permitted console senders, while player-only commands return no console suggestions.
|
||||
- Verified syntax, eligibility, membership, permissions, prefix filtering, compiler warnings, tests, and packaging with `./gradlew clean check jar`.
|
||||
|
||||
## 2026-08-21 — Contextual command completion started
|
||||
|
||||
- US-018 begins a test-first replacement of Bukkit's player-name fallback with syntax-aware Tyrant, Vigilante, and administrative command suggestions.
|
||||
- Player arguments will be filtered by the applicable assignment or membership rules, while administrative suggestions remain permission-gated.
|
||||
|
||||
## 2026-08-21 — Single-mob Tamer custody completed
|
||||
|
||||
- Completed US-007 with an authoritative one-mob custody limit that rejects a second capture before inventory, entity, or state mutation and tells the Tamer to release the held mob first.
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
15. [US-015: Manage a reign through the Tyrant control panel](us-015-manage-tyrant-control-panel.md)
|
||||
16. [US-016: Manage Followers through the Vigilante control panel](us-016-manage-vigilante-control-panel.md)
|
||||
17. [US-017: Use bound role control items](us-017-use-bound-role-control-items.md)
|
||||
18. [US-018: Complete Tyrant commands contextually](us-018-complete-commands-contextually.md)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
type: User Story
|
||||
title: "US-018: Complete Tyrant commands contextually"
|
||||
description: Replace Bukkit's player-name fallback with valid context-aware command suggestions.
|
||||
status: done
|
||||
---
|
||||
|
||||
# US-018: Complete Tyrant commands contextually
|
||||
|
||||
As a **player or administrator**, I want contextual command suggestions so that I can discover valid Tyrant commands and arguments.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] `/tyrant` suggests its available subcommands instead of arbitrary player names.
|
||||
- [x] `/tyrant buy` suggests Tyrant unlock names.
|
||||
- [x] `/tyrant assign` suggests `assassin`, `fixer`, and `tamer`, followed by eligible online players.
|
||||
- [x] `/tyrant relinquish` suggests `confirm`.
|
||||
- [x] `/vigilante` suggests its available subcommands.
|
||||
- [x] `/vigilante invite` suggests eligible online recruitment candidates.
|
||||
- [x] `/vigilante dismiss` suggests current online Followers.
|
||||
- [x] `/tyrantadmin` suggestions are visible only to senders with `spigottyrant.admin`.
|
||||
- [x] `/tyrantadmin start` suggests online Tyrant and Vigilante candidates.
|
||||
- [x] `/tyrantadmin reset` suggests `confirm`.
|
||||
- [x] Suggestions are filtered case-insensitively by the partially typed argument.
|
||||
- [x] Suggestions never include syntactically invalid options for the current argument position.
|
||||
- [x] Console completion works for administrative commands without exposing player-only commands as executable console actions.
|
||||
- [x] Existing command execution behavior remains unchanged.
|
||||
|
||||
## Validation
|
||||
|
||||
Automated tests verify root syntax, unlocks, classes, confirmations, eligible recruits, current Followers, permission gating, online administrative candidates, argument positions, and case-insensitive prefix filtering. The complete `./gradlew clean check jar` lifecycle passes.
|
||||
|
||||
## Related
|
||||
|
||||
- [Start, pause, and administer the game](us-001-start-pause-and-administer.md)
|
||||
- [Assign unlocked classes](us-004-assign-unlocked-classes.md)
|
||||
- [Support the Vigilante and Followers](us-008-support-vigilante-and-followers.md)
|
||||
- [Manage a reign through the Tyrant control panel](us-015-manage-tyrant-control-panel.md)
|
||||
- [Manage Followers through the Vigilante control panel](us-016-manage-vigilante-control-panel.md)
|
||||
@@ -136,6 +136,14 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
|
||||
settings.pendingSelectionTimeout(),
|
||||
random
|
||||
));
|
||||
Objects.requireNonNull(getCommand("tyrant"), "Missing tyrant command metadata")
|
||||
.setTabCompleter(new TyrantTabCompleter(stateManager, onlinePlayers));
|
||||
Objects.requireNonNull(getCommand("vigilante"), "Missing vigilante metadata")
|
||||
.setTabCompleter(new VigilanteTabCompleter(
|
||||
stateManager, followers, onlinePlayers
|
||||
));
|
||||
Objects.requireNonNull(getCommand("tyrantadmin"), "Missing tyrantadmin metadata")
|
||||
.setTabCompleter(new TyrantAdminTabCompleter(onlinePlayers));
|
||||
getServer().getPluginManager().registerEvents(
|
||||
new PlayerJoinListener(stateManager, clock, tyrantPresentation),
|
||||
this
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class TabSuggestions {
|
||||
private TabSuggestions() {
|
||||
}
|
||||
|
||||
public static List<String> matching(String prefix, Collection<String> candidates) {
|
||||
String normalized = prefix == null ? "" : prefix.toLowerCase(Locale.ROOT);
|
||||
return candidates.stream()
|
||||
.filter(candidate -> candidate.toLowerCase(Locale.ROOT).startsWith(normalized))
|
||||
.distinct()
|
||||
.sorted(String.CASE_INSENSITIVE_ORDER)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class TyrantAdminTabCompleter implements TabCompleter {
|
||||
private static final String PERMISSION = "spigottyrant.admin";
|
||||
private static final List<String> SUBCOMMANDS = List.of(
|
||||
"status", "start", "pause", "resume", "reset"
|
||||
);
|
||||
private final OnlinePlayerDirectory onlinePlayers;
|
||||
|
||||
public TyrantAdminTabCompleter(OnlinePlayerDirectory onlinePlayers) {
|
||||
this.onlinePlayers = onlinePlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (!sender.hasPermission(PERMISSION) || arguments.length == 0) {
|
||||
return List.of();
|
||||
}
|
||||
if (arguments.length == 1) {
|
||||
return TabSuggestions.matching(arguments[0], SUBCOMMANDS);
|
||||
}
|
||||
String subcommand = arguments[0].toLowerCase(java.util.Locale.ROOT);
|
||||
if (arguments.length == 2) {
|
||||
return switch (subcommand) {
|
||||
case "start" -> TabSuggestions.matching(arguments[1], onlineNames(null));
|
||||
case "reset" -> TabSuggestions.matching(arguments[1], List.of("confirm"));
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
if (arguments.length == 3 && subcommand.equals("start")) {
|
||||
return TabSuggestions.matching(arguments[2], onlineNames(arguments[1]));
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private List<String> onlineNames(String excludedName) {
|
||||
List<String> names = new ArrayList<>();
|
||||
for (UUID playerId : onlinePlayers.onlinePlayerIds()) {
|
||||
Player player = onlinePlayers.findById(playerId);
|
||||
if (player != null && (excludedName == null
|
||||
|| !player.getName().equalsIgnoreCase(excludedName))) {
|
||||
names.add(player.getName());
|
||||
}
|
||||
}
|
||||
return List.copyOf(names);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class TyrantTabCompleter implements TabCompleter {
|
||||
private static final List<String> SUBCOMMANDS = List.of(
|
||||
"menu", "status", "choices", "buy", "assign", "item", "intelligence",
|
||||
"optout", "optin", "relinquish"
|
||||
);
|
||||
private static final List<String> UNLOCKS = java.util.Arrays.stream(TyrantUnlock.values())
|
||||
.map(value -> value.name().toLowerCase(java.util.Locale.ROOT))
|
||||
.toList();
|
||||
private static final List<String> CLASSES = List.of("assassin", "fixer", "tamer");
|
||||
private final TyrantStateManager stateManager;
|
||||
private final OnlinePlayerDirectory onlinePlayers;
|
||||
|
||||
public TyrantTabCompleter(
|
||||
TyrantStateManager stateManager,
|
||||
OnlinePlayerDirectory onlinePlayers
|
||||
) {
|
||||
this.stateManager = stateManager;
|
||||
this.onlinePlayers = onlinePlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (!(sender instanceof Player player) || arguments.length == 0) {
|
||||
return List.of();
|
||||
}
|
||||
if (arguments.length == 1) {
|
||||
return TabSuggestions.matching(arguments[0], SUBCOMMANDS);
|
||||
}
|
||||
String subcommand = arguments[0].toLowerCase(java.util.Locale.ROOT);
|
||||
if (arguments.length == 2) {
|
||||
return switch (subcommand) {
|
||||
case "buy" -> TabSuggestions.matching(arguments[1], UNLOCKS);
|
||||
case "assign" -> TabSuggestions.matching(arguments[1], CLASSES);
|
||||
case "relinquish" -> TabSuggestions.matching(
|
||||
arguments[1], List.of("confirm")
|
||||
);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
if (arguments.length == 3 && subcommand.equals("assign")) {
|
||||
return TabSuggestions.matching(arguments[2], eligibleAssignees(player));
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private List<String> eligibleAssignees(Player tyrant) {
|
||||
Map<UUID, PlayerState> states = stateManager.players();
|
||||
GameState game = stateManager.game();
|
||||
Set<UUID> online = onlinePlayers.onlinePlayerIds();
|
||||
List<String> names = new ArrayList<>();
|
||||
for (UUID playerId : online) {
|
||||
if (playerId.equals(tyrant.getUniqueId())
|
||||
|| game != null && game.tyrantId().filter(playerId::equals).isPresent()) {
|
||||
continue;
|
||||
}
|
||||
PlayerState state = states.get(playerId);
|
||||
if (state != null && state.optedOutUntil().isPresent()) {
|
||||
continue;
|
||||
}
|
||||
Player candidate = onlinePlayers.findById(playerId);
|
||||
if (candidate != null) {
|
||||
names.add(candidate.getName());
|
||||
}
|
||||
}
|
||||
return List.copyOf(names);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class VigilanteTabCompleter implements TabCompleter {
|
||||
private static final List<String> SUBCOMMANDS = List.of(
|
||||
"menu", "item", "invite", "accept", "dismiss", "leave"
|
||||
);
|
||||
private final TyrantStateManager stateManager;
|
||||
private final FollowerService followers;
|
||||
private final OnlinePlayerDirectory onlinePlayers;
|
||||
|
||||
public VigilanteTabCompleter(
|
||||
TyrantStateManager stateManager,
|
||||
FollowerService followers,
|
||||
OnlinePlayerDirectory onlinePlayers
|
||||
) {
|
||||
this.stateManager = stateManager;
|
||||
this.followers = followers;
|
||||
this.onlinePlayers = onlinePlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (!(sender instanceof Player player) || arguments.length == 0) {
|
||||
return List.of();
|
||||
}
|
||||
if (arguments.length == 1) {
|
||||
return TabSuggestions.matching(arguments[0], SUBCOMMANDS);
|
||||
}
|
||||
if (arguments.length != 2) {
|
||||
return List.of();
|
||||
}
|
||||
return switch (arguments[0].toLowerCase(java.util.Locale.ROOT)) {
|
||||
case "invite" -> TabSuggestions.matching(
|
||||
arguments[1], eligibleInvitees(player)
|
||||
);
|
||||
case "dismiss" -> TabSuggestions.matching(
|
||||
arguments[1], currentFollowers(player)
|
||||
);
|
||||
default -> List.of();
|
||||
};
|
||||
}
|
||||
|
||||
private List<String> eligibleInvitees(Player vigilante) {
|
||||
GameState game = stateManager.game();
|
||||
Map<UUID, PlayerState> states = stateManager.players();
|
||||
Set<UUID> pending = followers.invitedPlayerIds(vigilante.getUniqueId());
|
||||
List<String> names = new ArrayList<>();
|
||||
for (UUID playerId : onlinePlayers.onlinePlayerIds()) {
|
||||
PlayerState state = states.get(playerId);
|
||||
boolean assignedRole = game.tyrantId().filter(playerId::equals).isPresent()
|
||||
|| game.vigilanteId().filter(playerId::equals).isPresent();
|
||||
boolean ineligibleState = state != null
|
||||
&& (state.optedOutUntil().isPresent() || state.followerOf().isPresent());
|
||||
if (assignedRole || ineligibleState || pending.contains(playerId)) {
|
||||
continue;
|
||||
}
|
||||
Player candidate = onlinePlayers.findById(playerId);
|
||||
if (candidate != null) {
|
||||
names.add(candidate.getName());
|
||||
}
|
||||
}
|
||||
return List.copyOf(names);
|
||||
}
|
||||
|
||||
private List<String> currentFollowers(Player vigilante) {
|
||||
Map<UUID, PlayerState> states = stateManager.players();
|
||||
List<String> names = new ArrayList<>();
|
||||
for (UUID playerId : onlinePlayers.onlinePlayerIds()) {
|
||||
PlayerState state = states.get(playerId);
|
||||
if (state == null
|
||||
|| state.followerOf().filter(vigilante.getUniqueId()::equals).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Player candidate = onlinePlayers.findById(playerId);
|
||||
if (candidate != null) {
|
||||
names.add(candidate.getName());
|
||||
}
|
||||
}
|
||||
return List.copyOf(names);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantAdminTabCompleterTest {
|
||||
@Test
|
||||
void permissionGatesAdministrativeSyntaxAndOnlineCandidates() {
|
||||
UUID alphaId = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
UUID betaId = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
Player alpha = player(alphaId, "Alpha");
|
||||
Player beta = player(betaId, "Beta");
|
||||
OnlinePlayerDirectory directory = mock(OnlinePlayerDirectory.class);
|
||||
when(directory.onlinePlayerIds()).thenReturn(Set.of(alphaId, betaId));
|
||||
when(directory.findById(alphaId)).thenReturn(alpha);
|
||||
when(directory.findById(betaId)).thenReturn(beta);
|
||||
TyrantAdminTabCompleter completer = new TyrantAdminTabCompleter(directory);
|
||||
Command command = mock(Command.class);
|
||||
CommandSender denied = mock(CommandSender.class);
|
||||
CommandSender admin = mock(CommandSender.class);
|
||||
when(admin.hasPermission("spigottyrant.admin")).thenReturn(true);
|
||||
|
||||
assertEquals(java.util.List.of(), completer.onTabComplete(
|
||||
denied, command, "tyrantadmin", new String[] {""}
|
||||
));
|
||||
assertTrue(completer.onTabComplete(
|
||||
admin, command, "tyrantadmin", new String[] {""}
|
||||
).containsAll(java.util.List.of("status", "start", "pause", "resume", "reset")));
|
||||
assertEquals(java.util.List.of("Alpha", "Beta"), completer.onTabComplete(
|
||||
admin, command, "tyrantadmin", new String[] {"start", ""}
|
||||
));
|
||||
assertEquals(java.util.List.of("Beta"), completer.onTabComplete(
|
||||
admin, command, "tyrantadmin", new String[] {"start", "Alpha", ""}
|
||||
));
|
||||
assertEquals(java.util.List.of("confirm"), completer.onTabComplete(
|
||||
admin, command, "tyrantadmin", new String[] {"reset", "c"}
|
||||
));
|
||||
}
|
||||
|
||||
private static Player player(UUID id, String name) {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(id);
|
||||
when(player.getName()).thenReturn(name);
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantTabCompleterTest {
|
||||
@Test
|
||||
void rootBuyAssignAndConfirmationSuggestionsFollowSyntaxAndPrefix() {
|
||||
TyrantTabCompleter completer = new TyrantTabCompleter(
|
||||
mock(TyrantStateManager.class), mock(OnlinePlayerDirectory.class)
|
||||
);
|
||||
Player player = mock(Player.class);
|
||||
Command command = mock(Command.class);
|
||||
|
||||
List<String> root = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {""}
|
||||
);
|
||||
List<String> buy = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"buy", "ro"}
|
||||
);
|
||||
List<String> assign = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"assign", "f"}
|
||||
);
|
||||
List<String> confirm = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"relinquish", "c"}
|
||||
);
|
||||
|
||||
assertTrue(root.containsAll(List.of("menu", "status", "buy", "assign", "item")));
|
||||
assertFalse(root.contains("SomePlayer"));
|
||||
assertEquals(List.of("roster_intelligence"), buy);
|
||||
assertEquals(List.of("fixer"), assign);
|
||||
assertEquals(List.of("confirm"), confirm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class VigilanteTabCompleterTest {
|
||||
@Test
|
||||
void rootAndPlayerArgumentsUseMembershipEligibility() {
|
||||
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
UUID vigilanteId = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
UUID eligibleId = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
UUID followerId = UUID.fromString("44444444-4444-4444-4444-444444444444");
|
||||
PlayerState eligible = PlayerState.newPlayer(eligibleId, "Eligible");
|
||||
PlayerState follower = follower(followerId, "Follower", vigilanteId);
|
||||
TyrantStateManager manager = mock(TyrantStateManager.class);
|
||||
when(manager.game()).thenReturn(new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.of(vigilanteId),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
0, 0, Set.of()
|
||||
));
|
||||
when(manager.players()).thenReturn(Map.of(eligibleId, eligible, followerId, follower));
|
||||
OnlinePlayerDirectory directory = mock(OnlinePlayerDirectory.class);
|
||||
when(directory.onlinePlayerIds()).thenReturn(Set.of(eligibleId, followerId, tyrantId));
|
||||
Player eligiblePlayer = player(eligibleId, "Eligible");
|
||||
Player followerPlayer = player(followerId, "Follower");
|
||||
Player tyrantPlayer = player(tyrantId, "Tyrant");
|
||||
when(directory.findById(eligibleId)).thenReturn(eligiblePlayer);
|
||||
when(directory.findById(followerId)).thenReturn(followerPlayer);
|
||||
when(directory.findById(tyrantId)).thenReturn(tyrantPlayer);
|
||||
VigilanteTabCompleter completer = new VigilanteTabCompleter(
|
||||
manager, new FollowerService(), directory
|
||||
);
|
||||
Player vigilante = player(vigilanteId, "Vigilante");
|
||||
Command command = mock(Command.class);
|
||||
|
||||
assertTrue(completer.onTabComplete(
|
||||
vigilante, command, "vigilante", new String[] {""}
|
||||
).containsAll(java.util.List.of("menu", "item", "invite", "dismiss")));
|
||||
assertEquals(java.util.List.of("Eligible"), completer.onTabComplete(
|
||||
vigilante, command, "vigilante", new String[] {"invite", ""}
|
||||
));
|
||||
assertEquals(java.util.List.of("Follower"), completer.onTabComplete(
|
||||
vigilante, command, "vigilante", new String[] {"dismiss", ""}
|
||||
));
|
||||
}
|
||||
|
||||
private static Player player(UUID id, String name) {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(id);
|
||||
when(player.getName()).thenReturn(name);
|
||||
return player;
|
||||
}
|
||||
|
||||
private static PlayerState follower(UUID id, String name, UUID vigilanteId) {
|
||||
PlayerState state = PlayerState.newPlayer(id, name);
|
||||
return new PlayerState(
|
||||
id, name, state.lastLogin(), state.optedOutUntil(), state.tyrantClass(),
|
||||
Optional.of(vigilanteId), state.cooldownEnds(), state.readyAbilityItems(),
|
||||
state.capturedMobs()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user