feat(commands): add contextual tab completion
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user