Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Refactor built-in commands to use Brigadier (#1208)
* Refactor /velocity command to use Brigadier * Added default usage message and code cleanup * Reimplemented callback subcommand * Fixed execution of the callback subcommand when all permissions of the main command subcommands are denied * Migrated callback subcommand to its own command * Migrated server command * GList, Send and Server command cleanup --------- Co-authored-by: Adrian <adriangonzalesval@gmail.com>
Dieser Commit ist enthalten in:
Ursprung
c3583e182c
Commit
b9b11665b9
@ -37,6 +37,7 @@ import com.velocitypowered.api.util.Favicon;
|
|||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import com.velocitypowered.proxy.command.VelocityCommandManager;
|
import com.velocitypowered.proxy.command.VelocityCommandManager;
|
||||||
|
import com.velocitypowered.proxy.command.builtin.CallbackCommand;
|
||||||
import com.velocitypowered.proxy.command.builtin.GlistCommand;
|
import com.velocitypowered.proxy.command.builtin.GlistCommand;
|
||||||
import com.velocitypowered.proxy.command.builtin.SendCommand;
|
import com.velocitypowered.proxy.command.builtin.SendCommand;
|
||||||
import com.velocitypowered.proxy.command.builtin.ServerCommand;
|
import com.velocitypowered.proxy.command.builtin.ServerCommand;
|
||||||
@ -223,8 +224,9 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
cm.logChannelInformation();
|
cm.logChannelInformation();
|
||||||
|
|
||||||
// Initialize commands first
|
// Initialize commands first
|
||||||
commandManager.register("velocity", new VelocityCommand(this));
|
commandManager.register(VelocityCommand.create(this));
|
||||||
commandManager.register("server", new ServerCommand(this));
|
commandManager.register(CallbackCommand.create());
|
||||||
|
commandManager.register(ServerCommand.create(this));
|
||||||
commandManager.register("shutdown", ShutdownCommand.command(this),
|
commandManager.register("shutdown", ShutdownCommand.command(this),
|
||||||
"end", "stop");
|
"end", "stop");
|
||||||
new GlistCommand(this).register();
|
new GlistCommand(this).register();
|
||||||
|
@ -33,7 +33,7 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
|||||||
public class ClickCallbackManager {
|
public class ClickCallbackManager {
|
||||||
public static final ClickCallbackManager INSTANCE = new ClickCallbackManager();
|
public static final ClickCallbackManager INSTANCE = new ClickCallbackManager();
|
||||||
|
|
||||||
static final String COMMAND = "/velocity callback ";
|
static final String COMMAND = "/velocity:callback ";
|
||||||
|
|
||||||
private final Cache<UUID, RegisteredCallback> registrations = Caffeine.newBuilder()
|
private final Cache<UUID, RegisteredCallback> registrations = Caffeine.newBuilder()
|
||||||
.expireAfter(new Expiry<UUID, RegisteredCallback>() {
|
.expireAfter(new Expiry<UUID, RegisteredCallback>() {
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Velocity Contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.velocitypowered.proxy.command.builtin;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
|
import com.velocitypowered.proxy.adventure.ClickCallbackManager;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback Command.
|
||||||
|
*/
|
||||||
|
public final class CallbackCommand implements Command<CommandSource> {
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MissingJavadocMethod")
|
||||||
|
public static BrigadierCommand create() {
|
||||||
|
final LiteralCommandNode<CommandSource> node = BrigadierCommand
|
||||||
|
.literalArgumentBuilder("velocity:callback")
|
||||||
|
.then(BrigadierCommand.requiredArgumentBuilder("id", StringArgumentType.word())
|
||||||
|
.executes(new CallbackCommand()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BrigadierCommand(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(final CommandContext<CommandSource> context) {
|
||||||
|
final String providedId = StringArgumentType.getString(context, "id");
|
||||||
|
final UUID id;
|
||||||
|
try {
|
||||||
|
id = UUID.fromString(providedId);
|
||||||
|
} catch (final IllegalArgumentException ignored) {
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickCallbackManager.INSTANCE.runCallback(context.getSource(), id);
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@ -20,12 +20,11 @@ package com.velocitypowered.proxy.command.builtin;
|
|||||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
|
||||||
import com.velocitypowered.api.command.BrigadierCommand;
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.permission.Tristate;
|
import com.velocitypowered.api.permission.Tristate;
|
||||||
@ -34,7 +33,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
import net.kyori.adventure.text.TranslatableComponent;
|
import net.kyori.adventure.text.TranslatableComponent;
|
||||||
@ -57,20 +55,19 @@ public class GlistCommand {
|
|||||||
* Registers this command.
|
* Registers this command.
|
||||||
*/
|
*/
|
||||||
public void register() {
|
public void register() {
|
||||||
LiteralCommandNode<CommandSource> totalNode = LiteralArgumentBuilder
|
final LiteralArgumentBuilder<CommandSource> rootNode = BrigadierCommand
|
||||||
.<CommandSource>literal("glist")
|
.literalArgumentBuilder("glist")
|
||||||
.requires(source ->
|
.requires(source ->
|
||||||
source.getPermissionValue("velocity.command.glist") == Tristate.TRUE)
|
source.getPermissionValue("velocity.command.glist") == Tristate.TRUE)
|
||||||
.executes(this::totalCount)
|
.executes(this::totalCount);
|
||||||
.build();
|
final ArgumentCommandNode<CommandSource, String> serverNode = BrigadierCommand
|
||||||
ArgumentCommandNode<CommandSource, String> serverNode = RequiredArgumentBuilder
|
.requiredArgumentBuilder(SERVER_ARG, StringArgumentType.string())
|
||||||
.<CommandSource, String>argument(SERVER_ARG, StringArgumentType.string())
|
|
||||||
.suggests((context, builder) -> {
|
.suggests((context, builder) -> {
|
||||||
String argument = context.getArguments().containsKey(SERVER_ARG)
|
final String argument = context.getArguments().containsKey(SERVER_ARG)
|
||||||
? context.getArgument(SERVER_ARG, String.class)
|
? context.getArgument(SERVER_ARG, String.class)
|
||||||
: "";
|
: "";
|
||||||
for (RegisteredServer server : server.getAllServers()) {
|
for (RegisteredServer server : server.getAllServers()) {
|
||||||
String serverName = server.getServerInfo().getName();
|
final String serverName = server.getServerInfo().getName();
|
||||||
if (serverName.regionMatches(true, 0, argument, 0, argument.length())) {
|
if (serverName.regionMatches(true, 0, argument, 0, argument.length())) {
|
||||||
builder.suggest(serverName);
|
builder.suggest(serverName);
|
||||||
}
|
}
|
||||||
@ -82,14 +79,14 @@ public class GlistCommand {
|
|||||||
})
|
})
|
||||||
.executes(this::serverCount)
|
.executes(this::serverCount)
|
||||||
.build();
|
.build();
|
||||||
totalNode.addChild(serverNode);
|
rootNode.then(serverNode);
|
||||||
server.getCommandManager().register(new BrigadierCommand(totalNode));
|
server.getCommandManager().register(new BrigadierCommand(rootNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int totalCount(final CommandContext<CommandSource> context) {
|
private int totalCount(final CommandContext<CommandSource> context) {
|
||||||
final CommandSource source = context.getSource();
|
final CommandSource source = context.getSource();
|
||||||
sendTotalProxyCount(source);
|
sendTotalProxyCount(source);
|
||||||
source.sendMessage(Identity.nil(),
|
source.sendMessage(
|
||||||
Component.translatable("velocity.command.glist-view-all", NamedTextColor.YELLOW));
|
Component.translatable("velocity.command.glist-view-all", NamedTextColor.YELLOW));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -98,38 +95,42 @@ public class GlistCommand {
|
|||||||
final CommandSource source = context.getSource();
|
final CommandSource source = context.getSource();
|
||||||
final String serverName = getString(context, SERVER_ARG);
|
final String serverName = getString(context, SERVER_ARG);
|
||||||
if (serverName.equalsIgnoreCase("all")) {
|
if (serverName.equalsIgnoreCase("all")) {
|
||||||
for (RegisteredServer server : BuiltinCommandUtil.sortedServerList(server)) {
|
for (final RegisteredServer server : BuiltinCommandUtil.sortedServerList(server)) {
|
||||||
sendServerPlayers(source, server, true);
|
sendServerPlayers(source, server, true);
|
||||||
}
|
}
|
||||||
sendTotalProxyCount(source);
|
sendTotalProxyCount(source);
|
||||||
} else {
|
} else {
|
||||||
Optional<RegisteredServer> registeredServer = server.getServer(serverName);
|
final Optional<RegisteredServer> registeredServer = server.getServer(serverName);
|
||||||
if (!registeredServer.isPresent()) {
|
if (registeredServer.isEmpty()) {
|
||||||
source.sendMessage(Identity.nil(),
|
source.sendMessage(
|
||||||
CommandMessages.SERVER_DOES_NOT_EXIST.args(Component.text(serverName)));
|
CommandMessages.SERVER_DOES_NOT_EXIST
|
||||||
|
.arguments(Component.text(serverName)));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sendServerPlayers(source, registeredServer.get(), false);
|
sendServerPlayers(source, registeredServer.get(), false);
|
||||||
}
|
}
|
||||||
return 1;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendTotalProxyCount(CommandSource target) {
|
private void sendTotalProxyCount(CommandSource target) {
|
||||||
int online = server.getPlayerCount();
|
final int online = server.getPlayerCount();
|
||||||
TranslatableComponent msg = online == 1
|
final TranslatableComponent.Builder msg = Component.translatable()
|
||||||
? Component.translatable("velocity.command.glist-player-singular")
|
.key(online == 1
|
||||||
: Component.translatable("velocity.command.glist-player-plural");
|
? "velocity.command.glist-player-singular"
|
||||||
target.sendMessage(msg.color(NamedTextColor.YELLOW)
|
: "velocity.command.glist-player-plural"
|
||||||
.args(Component.text(Integer.toString(online), NamedTextColor.GREEN)));
|
).color(NamedTextColor.YELLOW)
|
||||||
|
.arguments(Component.text(Integer.toString(online), NamedTextColor.GREEN));
|
||||||
|
target.sendMessage(msg.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendServerPlayers(CommandSource target, RegisteredServer server, boolean fromAll) {
|
private void sendServerPlayers(final CommandSource target,
|
||||||
List<Player> onServer = ImmutableList.copyOf(server.getPlayersConnected());
|
final RegisteredServer server, final boolean fromAll) {
|
||||||
|
final List<Player> onServer = ImmutableList.copyOf(server.getPlayersConnected());
|
||||||
if (onServer.isEmpty() && fromAll) {
|
if (onServer.isEmpty() && fromAll) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextComponent.Builder builder = Component.text()
|
final TextComponent.Builder builder = Component.text()
|
||||||
.append(Component.text("[" + server.getServerInfo().getName() + "] ",
|
.append(Component.text("[" + server.getServerInfo().getName() + "] ",
|
||||||
NamedTextColor.DARK_AQUA))
|
NamedTextColor.DARK_AQUA))
|
||||||
.append(Component.text("(" + onServer.size() + ")", NamedTextColor.GRAY))
|
.append(Component.text("(" + onServer.size() + ")", NamedTextColor.GRAY))
|
||||||
@ -137,7 +138,7 @@ public class GlistCommand {
|
|||||||
.resetStyle();
|
.resetStyle();
|
||||||
|
|
||||||
for (int i = 0; i < onServer.size(); i++) {
|
for (int i = 0; i < onServer.size(); i++) {
|
||||||
Player player = onServer.get(i);
|
final Player player = onServer.get(i);
|
||||||
builder.append(Component.text(player.getUsername()));
|
builder.append(Component.text(player.getUsername()));
|
||||||
|
|
||||||
if (i + 1 < onServer.size()) {
|
if (i + 1 < onServer.size()) {
|
||||||
@ -145,6 +146,6 @@ public class GlistCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target.sendMessage(Identity.nil(), builder.build());
|
target.sendMessage(builder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.command.builtin;
|
package com.velocitypowered.proxy.command.builtin;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
|
||||||
import com.velocitypowered.api.command.BrigadierCommand;
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.permission.Tristate;
|
import com.velocitypowered.api.permission.Tristate;
|
||||||
@ -51,20 +51,19 @@ public class SendCommand {
|
|||||||
* Registers this command.
|
* Registers this command.
|
||||||
*/
|
*/
|
||||||
public void register() {
|
public void register() {
|
||||||
LiteralCommandNode<CommandSource> totalNode = LiteralArgumentBuilder
|
final LiteralArgumentBuilder<CommandSource> rootNode = BrigadierCommand
|
||||||
.<CommandSource>literal("send")
|
.literalArgumentBuilder("send")
|
||||||
.requires(source ->
|
.requires(source ->
|
||||||
source.getPermissionValue("velocity.command.send") == Tristate.TRUE)
|
source.getPermissionValue("velocity.command.send") == Tristate.TRUE)
|
||||||
.executes(this::usage)
|
.executes(this::usage);
|
||||||
.build();
|
final RequiredArgumentBuilder<CommandSource, String> playerNode = BrigadierCommand
|
||||||
ArgumentCommandNode<CommandSource, String> playerNode = RequiredArgumentBuilder
|
.requiredArgumentBuilder(PLAYER_ARG, StringArgumentType.word())
|
||||||
.<CommandSource, String>argument("player", StringArgumentType.word())
|
|
||||||
.suggests((context, builder) -> {
|
.suggests((context, builder) -> {
|
||||||
String argument = context.getArguments().containsKey(PLAYER_ARG)
|
final String argument = context.getArguments().containsKey(PLAYER_ARG)
|
||||||
? context.getArgument(PLAYER_ARG, String.class)
|
? context.getArgument(PLAYER_ARG, String.class)
|
||||||
: "";
|
: "";
|
||||||
for (Player player : server.getAllPlayers()) {
|
for (final Player player : server.getAllPlayers()) {
|
||||||
String playerName = player.getUsername();
|
final String playerName = player.getUsername();
|
||||||
if (playerName.regionMatches(true, 0, argument, 0, argument.length())) {
|
if (playerName.regionMatches(true, 0, argument, 0, argument.length())) {
|
||||||
builder.suggest(playerName);
|
builder.suggest(playerName);
|
||||||
}
|
}
|
||||||
@ -78,16 +77,15 @@ public class SendCommand {
|
|||||||
}
|
}
|
||||||
return builder.buildFuture();
|
return builder.buildFuture();
|
||||||
})
|
})
|
||||||
.executes(this::usage)
|
.executes(this::usage);
|
||||||
.build();
|
final ArgumentCommandNode<CommandSource, String> serverNode = BrigadierCommand
|
||||||
ArgumentCommandNode<CommandSource, String> serverNode = RequiredArgumentBuilder
|
.requiredArgumentBuilder(SERVER_ARG, StringArgumentType.word())
|
||||||
.<CommandSource, String>argument("server", StringArgumentType.word())
|
|
||||||
.suggests((context, builder) -> {
|
.suggests((context, builder) -> {
|
||||||
String argument = context.getArguments().containsKey(SERVER_ARG)
|
final String argument = context.getArguments().containsKey(SERVER_ARG)
|
||||||
? context.getArgument(SERVER_ARG, String.class)
|
? context.getArgument(SERVER_ARG, String.class)
|
||||||
: "";
|
: "";
|
||||||
for (RegisteredServer server : server.getAllServers()) {
|
for (final RegisteredServer server : server.getAllServers()) {
|
||||||
String serverName = server.getServerInfo().getName();
|
final String serverName = server.getServerInfo().getName();
|
||||||
if (serverName.regionMatches(true, 0, argument, 0, argument.length())) {
|
if (serverName.regionMatches(true, 0, argument, 0, argument.length())) {
|
||||||
builder.suggest(server.getServerInfo().getName());
|
builder.suggest(server.getServerInfo().getName());
|
||||||
}
|
}
|
||||||
@ -96,66 +94,68 @@ public class SendCommand {
|
|||||||
})
|
})
|
||||||
.executes(this::send)
|
.executes(this::send)
|
||||||
.build();
|
.build();
|
||||||
totalNode.addChild(playerNode);
|
playerNode.then(serverNode);
|
||||||
playerNode.addChild(serverNode);
|
rootNode.then(playerNode.build());
|
||||||
server.getCommandManager().register(new BrigadierCommand(totalNode));
|
server.getCommandManager().register(new BrigadierCommand(rootNode.build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int usage(CommandContext<CommandSource> context) {
|
private int usage(final CommandContext<CommandSource> context) {
|
||||||
context.getSource().sendMessage(
|
context.getSource().sendMessage(
|
||||||
Component.translatable("velocity.command.send-usage", NamedTextColor.YELLOW)
|
Component.translatable("velocity.command.send-usage", NamedTextColor.YELLOW)
|
||||||
);
|
);
|
||||||
return 1;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int send(CommandContext<CommandSource> context) {
|
private int send(final CommandContext<CommandSource> context) {
|
||||||
String serverName = context.getArgument(SERVER_ARG, String.class);
|
final String serverName = context.getArgument(SERVER_ARG, String.class);
|
||||||
String player = context.getArgument(PLAYER_ARG, String.class);
|
final String player = context.getArgument(PLAYER_ARG, String.class);
|
||||||
|
|
||||||
Optional<RegisteredServer> maybeServer = server.getServer(serverName);
|
final Optional<RegisteredServer> maybeServer = server.getServer(serverName);
|
||||||
|
|
||||||
if (maybeServer.isEmpty()) {
|
if (maybeServer.isEmpty()) {
|
||||||
context.getSource().sendMessage(
|
context.getSource().sendMessage(
|
||||||
CommandMessages.SERVER_DOES_NOT_EXIST.args(Component.text(serverName))
|
CommandMessages.SERVER_DOES_NOT_EXIST.arguments(Component.text(serverName))
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.getPlayer(player).isEmpty()
|
final RegisteredServer targetServer = maybeServer.get();
|
||||||
|
|
||||||
|
final Optional<Player> maybePlayer = server.getPlayer(player);
|
||||||
|
if (maybePlayer.isEmpty()
|
||||||
&& !Objects.equals(player, "all")
|
&& !Objects.equals(player, "all")
|
||||||
&& !Objects.equals(player, "current")) {
|
&& !Objects.equals(player, "current")) {
|
||||||
context.getSource().sendMessage(
|
context.getSource().sendMessage(
|
||||||
CommandMessages.PLAYER_NOT_FOUND.args(Component.text(player))
|
CommandMessages.PLAYER_NOT_FOUND.arguments(Component.text(player))
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.equals(player, "all")) {
|
if (Objects.equals(player, "all")) {
|
||||||
for (Player p : server.getAllPlayers()) {
|
for (final Player p : server.getAllPlayers()) {
|
||||||
p.createConnectionRequest(server.getServer(serverName).get()).fireAndForget();
|
p.createConnectionRequest(targetServer).fireAndForget();
|
||||||
}
|
}
|
||||||
return 1;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.equals(player, "current")) {
|
if (Objects.equals(player, "current")) {
|
||||||
if (!(context.getSource() instanceof Player)) {
|
if (!(context.getSource() instanceof Player source)) {
|
||||||
context.getSource().sendMessage(CommandMessages.PLAYERS_ONLY);
|
context.getSource().sendMessage(CommandMessages.PLAYERS_ONLY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player source = (Player) context.getSource();
|
final Optional<ServerConnection> connectedServer = source.getCurrentServer();
|
||||||
Optional<ServerConnection> connectedServer = source.getCurrentServer();
|
|
||||||
if (connectedServer.isPresent()) {
|
if (connectedServer.isPresent()) {
|
||||||
for (Player p : connectedServer.get().getServer().getPlayersConnected()) {
|
for (final Player p : connectedServer.get().getServer().getPlayersConnected()) {
|
||||||
p.createConnectionRequest(maybeServer.get()).fireAndForget();
|
p.createConnectionRequest(maybeServer.get()).fireAndForget();
|
||||||
}
|
}
|
||||||
return 1;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.getPlayer(player).get().createConnectionRequest(
|
// The player at this point must be present
|
||||||
maybeServer.get()).fireAndForget();
|
maybePlayer.orElseThrow().createConnectionRequest(targetServer).fireAndForget();
|
||||||
return 1;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,11 @@ package com.velocitypowered.proxy.command.builtin;
|
|||||||
|
|
||||||
import static net.kyori.adventure.text.event.HoverEvent.showText;
|
import static net.kyori.adventure.text.event.HoverEvent.showText;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.SimpleCommand;
|
|
||||||
import com.velocitypowered.api.permission.Tristate;
|
import com.velocitypowered.api.permission.Tristate;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
@ -30,8 +32,6 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
import net.kyori.adventure.text.TranslatableComponent;
|
import net.kyori.adventure.text.TranslatableComponent;
|
||||||
@ -41,50 +41,65 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
|||||||
/**
|
/**
|
||||||
* Implements Velocity's {@code /server} command.
|
* Implements Velocity's {@code /server} command.
|
||||||
*/
|
*/
|
||||||
public class ServerCommand implements SimpleCommand {
|
public final class ServerCommand {
|
||||||
|
private static final String SERVER_ARG = "server";
|
||||||
public static final int MAX_SERVERS_TO_LIST = 50;
|
public static final int MAX_SERVERS_TO_LIST = 50;
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
public ServerCommand(ProxyServer server) {
|
@SuppressWarnings("checkstyle:MissingJavadocMethod")
|
||||||
this.server = server;
|
public static BrigadierCommand create(final ProxyServer server) {
|
||||||
|
final LiteralCommandNode<CommandSource> node = BrigadierCommand
|
||||||
|
.literalArgumentBuilder("server")
|
||||||
|
.requires(src -> src instanceof Player
|
||||||
|
&& src.getPermissionValue("velocity.command.server") != Tristate.FALSE)
|
||||||
|
.executes(ctx -> {
|
||||||
|
final Player player = (Player) ctx.getSource();
|
||||||
|
outputServerInformation(player, server);
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
.then(BrigadierCommand.requiredArgumentBuilder(SERVER_ARG, StringArgumentType.word())
|
||||||
|
.suggests((ctx, builder) -> {
|
||||||
|
final String argument = ctx.getArguments().containsKey(SERVER_ARG)
|
||||||
|
? StringArgumentType.getString(ctx, SERVER_ARG)
|
||||||
|
: "";
|
||||||
|
for (final RegisteredServer sv : server.getAllServers()) {
|
||||||
|
final String serverName = sv.getServerInfo().getName();
|
||||||
|
if (serverName.regionMatches(true, 0, argument, 0, argument.length())) {
|
||||||
|
builder.suggest(serverName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.buildFuture();
|
||||||
|
})
|
||||||
|
.executes(ctx -> {
|
||||||
|
final Player player = (Player) ctx.getSource();
|
||||||
|
// Trying to connect to a server.
|
||||||
|
final String serverName = StringArgumentType.getString(ctx, SERVER_ARG);
|
||||||
|
final Optional<RegisteredServer> toConnect = server.getServer(serverName);
|
||||||
|
if (toConnect.isEmpty()) {
|
||||||
|
player.sendMessage(CommandMessages.SERVER_DOES_NOT_EXIST
|
||||||
|
.arguments(Component.text(serverName)));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.createConnectionRequest(toConnect.get()).fireAndForget();
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
).build();
|
||||||
|
|
||||||
|
return new BrigadierCommand(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static void outputServerInformation(final Player executor,
|
||||||
public void execute(final SimpleCommand.Invocation invocation) {
|
final ProxyServer server) {
|
||||||
final CommandSource source = invocation.source();
|
final String currentServer = executor.getCurrentServer()
|
||||||
final String[] args = invocation.arguments();
|
.map(ServerConnection::getServerInfo)
|
||||||
|
.map(ServerInfo::getName)
|
||||||
if (!(source instanceof Player)) {
|
.orElse("<unknown>");
|
||||||
source.sendMessage(CommandMessages.PLAYERS_ONLY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player player = (Player) source;
|
|
||||||
if (args.length == 1) {
|
|
||||||
// Trying to connect to a server.
|
|
||||||
String serverName = args[0];
|
|
||||||
Optional<RegisteredServer> toConnect = server.getServer(serverName);
|
|
||||||
if (toConnect.isEmpty()) {
|
|
||||||
player.sendMessage(CommandMessages.SERVER_DOES_NOT_EXIST.args(Component.text(serverName)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.createConnectionRequest(toConnect.get()).fireAndForget();
|
|
||||||
} else {
|
|
||||||
outputServerInformation(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void outputServerInformation(Player executor) {
|
|
||||||
String currentServer = executor.getCurrentServer().map(ServerConnection::getServerInfo)
|
|
||||||
.map(ServerInfo::getName).orElse("<unknown>");
|
|
||||||
executor.sendMessage(Component.translatable(
|
executor.sendMessage(Component.translatable(
|
||||||
"velocity.command.server-current-server",
|
"velocity.command.server-current-server",
|
||||||
NamedTextColor.YELLOW,
|
NamedTextColor.YELLOW,
|
||||||
Component.text(currentServer)));
|
Component.text(currentServer)));
|
||||||
|
|
||||||
List<RegisteredServer> servers = BuiltinCommandUtil.sortedServerList(server);
|
final List<RegisteredServer> servers = BuiltinCommandUtil.sortedServerList(server);
|
||||||
if (servers.size() > MAX_SERVERS_TO_LIST) {
|
if (servers.size() > MAX_SERVERS_TO_LIST) {
|
||||||
executor.sendMessage(Component.translatable(
|
executor.sendMessage(Component.translatable(
|
||||||
"velocity.command.server-too-many", NamedTextColor.RED));
|
"velocity.command.server-too-many", NamedTextColor.RED));
|
||||||
@ -92,12 +107,12 @@ public class ServerCommand implements SimpleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the list of servers as components
|
// Assemble the list of servers as components
|
||||||
TextComponent.Builder serverListBuilder = Component.text()
|
final TextComponent.Builder serverListBuilder = Component.text()
|
||||||
.append(Component.translatable("velocity.command.server-available",
|
.append(Component.translatable("velocity.command.server-available",
|
||||||
NamedTextColor.YELLOW))
|
NamedTextColor.YELLOW))
|
||||||
.append(Component.space());
|
.appendSpace();
|
||||||
for (int i = 0; i < servers.size(); i++) {
|
for (int i = 0; i < servers.size(); i++) {
|
||||||
RegisteredServer rs = servers.get(i);
|
final RegisteredServer rs = servers.get(i);
|
||||||
serverListBuilder.append(formatServerComponent(currentServer, rs));
|
serverListBuilder.append(formatServerComponent(currentServer, rs));
|
||||||
if (i != servers.size() - 1) {
|
if (i != servers.size() - 1) {
|
||||||
serverListBuilder.append(Component.text(", ", NamedTextColor.GRAY));
|
serverListBuilder.append(Component.text(", ", NamedTextColor.GRAY));
|
||||||
@ -107,22 +122,22 @@ public class ServerCommand implements SimpleCommand {
|
|||||||
executor.sendMessage(serverListBuilder.build());
|
executor.sendMessage(serverListBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextComponent formatServerComponent(String currentPlayerServer, RegisteredServer server) {
|
private static TextComponent formatServerComponent(final String currentPlayerServer,
|
||||||
ServerInfo serverInfo = server.getServerInfo();
|
final RegisteredServer server) {
|
||||||
TextComponent serverTextComponent = Component.text(serverInfo.getName());
|
final ServerInfo serverInfo = server.getServerInfo();
|
||||||
|
final TextComponent.Builder serverTextComponent = Component.text()
|
||||||
|
.content(serverInfo.getName());
|
||||||
|
|
||||||
int connectedPlayers = server.getPlayersConnected().size();
|
final int connectedPlayers = server.getPlayersConnected().size();
|
||||||
TranslatableComponent playersTextComponent;
|
final TranslatableComponent.Builder playersTextComponent = Component.translatable();
|
||||||
if (connectedPlayers == 1) {
|
if (connectedPlayers == 1) {
|
||||||
playersTextComponent = Component.translatable(
|
playersTextComponent.key("velocity.command.server-tooltip-player-online");
|
||||||
"velocity.command.server-tooltip-player-online");
|
|
||||||
} else {
|
} else {
|
||||||
playersTextComponent = Component.translatable(
|
playersTextComponent.key("velocity.command.server-tooltip-players-online");
|
||||||
"velocity.command.server-tooltip-players-online");
|
|
||||||
}
|
}
|
||||||
playersTextComponent = playersTextComponent.args(Component.text(connectedPlayers));
|
playersTextComponent.arguments(Component.text(connectedPlayers));
|
||||||
if (serverInfo.getName().equals(currentPlayerServer)) {
|
if (serverInfo.getName().equals(currentPlayerServer)) {
|
||||||
serverTextComponent = serverTextComponent.color(NamedTextColor.GREEN)
|
serverTextComponent.color(NamedTextColor.GREEN)
|
||||||
.hoverEvent(
|
.hoverEvent(
|
||||||
showText(
|
showText(
|
||||||
Component.translatable("velocity.command.server-tooltip-current-server")
|
Component.translatable("velocity.command.server-tooltip-current-server")
|
||||||
@ -130,7 +145,7 @@ public class ServerCommand implements SimpleCommand {
|
|||||||
.append(playersTextComponent))
|
.append(playersTextComponent))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
serverTextComponent = serverTextComponent.color(NamedTextColor.GRAY)
|
serverTextComponent.color(NamedTextColor.GRAY)
|
||||||
.clickEvent(ClickEvent.runCommand("/server " + serverInfo.getName()))
|
.clickEvent(ClickEvent.runCommand("/server " + serverInfo.getName()))
|
||||||
.hoverEvent(
|
.hoverEvent(
|
||||||
showText(
|
showText(
|
||||||
@ -139,28 +154,6 @@ public class ServerCommand implements SimpleCommand {
|
|||||||
.append(playersTextComponent))
|
.append(playersTextComponent))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return serverTextComponent;
|
return serverTextComponent.build();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> suggest(final SimpleCommand.Invocation invocation) {
|
|
||||||
final String[] currentArgs = invocation.arguments();
|
|
||||||
Stream<String> possibilities = server.getAllServers().stream()
|
|
||||||
.map(rs -> rs.getServerInfo().getName());
|
|
||||||
|
|
||||||
if (currentArgs.length == 0) {
|
|
||||||
return possibilities.collect(Collectors.toList());
|
|
||||||
} else if (currentArgs.length == 1) {
|
|
||||||
return possibilities
|
|
||||||
.filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
} else {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final SimpleCommand.Invocation invocation) {
|
|
||||||
return invocation.source().getPermissionValue("velocity.command.server") != Tristate.FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,16 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.command.builtin;
|
package com.velocitypowered.proxy.command.builtin;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.SimpleCommand;
|
|
||||||
import com.velocitypowered.api.permission.Tristate;
|
import com.velocitypowered.api.permission.Tristate;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
@ -31,7 +34,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.adventure.ClickCallbackManager;
|
|
||||||
import com.velocitypowered.proxy.util.InformationUtils;
|
import com.velocitypowered.proxy.util.InformationUtils;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -44,14 +46,12 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Set;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
@ -63,129 +63,71 @@ import net.kyori.adventure.text.format.TextColor;
|
|||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@code /velocity} command and friends.
|
* Implements the {@code /velocity} command and friends.
|
||||||
*/
|
*/
|
||||||
public class VelocityCommand implements SimpleCommand {
|
public final class VelocityCommand {
|
||||||
|
private static final String USAGE = "/velocity <%s>";
|
||||||
|
|
||||||
private interface SubCommand {
|
@SuppressWarnings("checkstyle:MissingJavadocMethod")
|
||||||
|
public static BrigadierCommand create(final VelocityServer server) {
|
||||||
void execute(final CommandSource source, final String @NonNull [] args);
|
final LiteralCommandNode<CommandSource> dump = BrigadierCommand.literalArgumentBuilder("dump")
|
||||||
|
.requires(source -> source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE)
|
||||||
default List<String> suggest(final CommandSource source, final String @NonNull [] currentArgs) {
|
.executes(new Dump(server))
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasPermission(final CommandSource source, final String @NonNull [] args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<String, SubCommand> commands;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the command object for /velocity.
|
|
||||||
*
|
|
||||||
* @param server the Velocity server
|
|
||||||
*/
|
|
||||||
public VelocityCommand(VelocityServer server) {
|
|
||||||
this.commands = ImmutableMap.<String, SubCommand>builder()
|
|
||||||
.put("version", new Info(server))
|
|
||||||
.put("plugins", new Plugins(server))
|
|
||||||
.put("reload", new Reload(server))
|
|
||||||
.put("dump", new Dump(server))
|
|
||||||
.put("heap", new Heap())
|
|
||||||
.put("callback", new Callback())
|
|
||||||
.build();
|
.build();
|
||||||
|
final LiteralCommandNode<CommandSource> heap = BrigadierCommand.literalArgumentBuilder("heap")
|
||||||
|
.requires(source -> source.getPermissionValue("velocity.command.heap") == Tristate.TRUE)
|
||||||
|
.executes(new Heap())
|
||||||
|
.build();
|
||||||
|
final LiteralCommandNode<CommandSource> info = BrigadierCommand.literalArgumentBuilder("info")
|
||||||
|
.requires(source -> source.getPermissionValue("velocity.command.info") != Tristate.FALSE)
|
||||||
|
.executes(new Info(server))
|
||||||
|
.build();
|
||||||
|
final LiteralCommandNode<CommandSource> plugins = BrigadierCommand
|
||||||
|
.literalArgumentBuilder("plugins")
|
||||||
|
.requires(source -> source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE)
|
||||||
|
.executes(new Plugins(server))
|
||||||
|
.build();
|
||||||
|
final LiteralCommandNode<CommandSource> reload = BrigadierCommand
|
||||||
|
.literalArgumentBuilder("reload")
|
||||||
|
.requires(source -> source.getPermissionValue("velocity.command.reload") == Tristate.TRUE)
|
||||||
|
.executes(new Reload(server))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final List<LiteralCommandNode<CommandSource>> commands = List
|
||||||
|
.of(dump, heap, info, plugins, reload);
|
||||||
|
return new BrigadierCommand(
|
||||||
|
commands.stream()
|
||||||
|
.reduce(
|
||||||
|
BrigadierCommand.literalArgumentBuilder("velocity")
|
||||||
|
.executes(ctx -> {
|
||||||
|
final CommandSource source = ctx.getSource();
|
||||||
|
final String availableCommands = commands.stream()
|
||||||
|
.filter(e -> e.getRequirement().test(source))
|
||||||
|
.map(LiteralCommandNode::getName)
|
||||||
|
.collect(Collectors.joining("|"));
|
||||||
|
final String commandText = USAGE.formatted(availableCommands);
|
||||||
|
source.sendMessage(Component.text(commandText, NamedTextColor.RED));
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
.requires(commands.stream()
|
||||||
|
.map(CommandNode::getRequirement)
|
||||||
|
.reduce(Predicate::or)
|
||||||
|
.orElseThrow()),
|
||||||
|
ArgumentBuilder::then,
|
||||||
|
ArgumentBuilder::then
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void usage(CommandSource source) {
|
private record Reload(VelocityServer server) implements Command<CommandSource> {
|
||||||
String availableCommands = commands.entrySet().stream()
|
|
||||||
.filter(e -> e.getValue().hasPermission(source, new String[0]))
|
|
||||||
.map(Map.Entry::getKey)
|
|
||||||
.collect(Collectors.joining("|"));
|
|
||||||
String commandText = "/velocity <" + availableCommands + ">";
|
|
||||||
source.sendMessage(Component.text(commandText, NamedTextColor.RED));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(final SimpleCommand.Invocation invocation) {
|
|
||||||
final CommandSource source = invocation.source();
|
|
||||||
final String[] args = invocation.arguments();
|
|
||||||
|
|
||||||
if (args.length == 0) {
|
|
||||||
usage(source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SubCommand command = commands.get(args[0].toLowerCase(Locale.US));
|
|
||||||
if (command == null) {
|
|
||||||
usage(source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@SuppressWarnings("nullness")
|
|
||||||
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
|
|
||||||
command.execute(source, actualArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> suggest(final SimpleCommand.Invocation invocation) {
|
|
||||||
final CommandSource source = invocation.source();
|
|
||||||
final String[] currentArgs = invocation.arguments();
|
|
||||||
|
|
||||||
if (currentArgs.length == 0) {
|
|
||||||
return commands.entrySet().stream()
|
|
||||||
.filter(e -> e.getValue().hasPermission(source, new String[0]))
|
|
||||||
.map(Map.Entry::getKey)
|
|
||||||
.collect(ImmutableList.toImmutableList());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentArgs.length == 1) {
|
|
||||||
return commands.entrySet().stream()
|
|
||||||
.filter(e -> e.getKey().regionMatches(true, 0, currentArgs[0], 0,
|
|
||||||
currentArgs[0].length()))
|
|
||||||
.filter(e -> e.getValue().hasPermission(source, new String[0]))
|
|
||||||
.map(Map.Entry::getKey)
|
|
||||||
.collect(ImmutableList.toImmutableList());
|
|
||||||
}
|
|
||||||
|
|
||||||
SubCommand command = commands.get(currentArgs[0].toLowerCase(Locale.US));
|
|
||||||
if (command == null) {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
@SuppressWarnings("nullness")
|
|
||||||
String[] actualArgs = Arrays.copyOfRange(currentArgs, 1, currentArgs.length);
|
|
||||||
return command.suggest(source, actualArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final SimpleCommand.Invocation invocation) {
|
|
||||||
final CommandSource source = invocation.source();
|
|
||||||
final String[] args = invocation.arguments();
|
|
||||||
|
|
||||||
if (args.length == 0) {
|
|
||||||
return commands.values().stream().anyMatch(e -> e.hasPermission(source, args));
|
|
||||||
}
|
|
||||||
SubCommand command = commands.get(args[0].toLowerCase(Locale.US));
|
|
||||||
if (command == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@SuppressWarnings("nullness")
|
|
||||||
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
|
|
||||||
return command.hasPermission(source, actualArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Reload implements SubCommand {
|
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(Reload.class);
|
private static final Logger logger = LogManager.getLogger(Reload.class);
|
||||||
private final VelocityServer server;
|
|
||||||
|
|
||||||
private Reload(VelocityServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public int run(final CommandContext<CommandSource> context) {
|
||||||
|
final CommandSource source = context.getSource();
|
||||||
try {
|
try {
|
||||||
if (server.reloadConfiguration()) {
|
if (server.reloadConfiguration()) {
|
||||||
source.sendMessage(Component.translatable("velocity.command.reload-success",
|
source.sendMessage(Component.translatable("velocity.command.reload-success",
|
||||||
@ -199,38 +141,28 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
source.sendMessage(Component.translatable("velocity.command.reload-failure",
|
source.sendMessage(Component.translatable("velocity.command.reload-failure",
|
||||||
NamedTextColor.RED));
|
NamedTextColor.RED));
|
||||||
}
|
}
|
||||||
}
|
return Command.SINGLE_SUCCESS;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.reload") == Tristate.TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Info implements SubCommand {
|
private record Info(ProxyServer server) implements Command<CommandSource> {
|
||||||
|
|
||||||
private static final TextColor VELOCITY_COLOR = TextColor.fromHexString("#09add3");
|
private static final TextColor VELOCITY_COLOR = TextColor.color(0x09add3);
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
private Info(ProxyServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public int run(final CommandContext<CommandSource> context) {
|
||||||
if (args.length != 0) {
|
final CommandSource source = context.getSource();
|
||||||
source.sendMessage(Component.text("/velocity version", NamedTextColor.RED));
|
final ProxyVersion version = server.getVersion();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyVersion version = server.getVersion();
|
final Component velocity = Component.text()
|
||||||
|
.content(version.getName() + " ")
|
||||||
Component velocity = Component.text().content(version.getName() + " ")
|
|
||||||
.decoration(TextDecoration.BOLD, true)
|
.decoration(TextDecoration.BOLD, true)
|
||||||
.color(VELOCITY_COLOR)
|
.color(VELOCITY_COLOR)
|
||||||
.append(Component.text(version.getVersion()).decoration(TextDecoration.BOLD, false))
|
.append(Component.text()
|
||||||
|
.content(version.getVersion())
|
||||||
|
.decoration(TextDecoration.BOLD, false))
|
||||||
.build();
|
.build();
|
||||||
Component copyright = Component
|
final Component copyright = Component
|
||||||
.translatable("velocity.command.version-copyright",
|
.translatable("velocity.command.version-copyright",
|
||||||
Component.text(version.getVendor()),
|
Component.text(version.getVendor()),
|
||||||
Component.text(version.getName()));
|
Component.text(version.getName()));
|
||||||
@ -238,14 +170,16 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
source.sendMessage(copyright);
|
source.sendMessage(copyright);
|
||||||
|
|
||||||
if (version.getName().equals("Velocity")) {
|
if (version.getName().equals("Velocity")) {
|
||||||
TextComponent embellishment = Component.text()
|
final TextComponent embellishment = Component.text()
|
||||||
.append(Component.text().content("velocitypowered.com")
|
.append(Component.text()
|
||||||
|
.content("velocitypowered.com")
|
||||||
.color(NamedTextColor.GREEN)
|
.color(NamedTextColor.GREEN)
|
||||||
.clickEvent(
|
.clickEvent(
|
||||||
ClickEvent.openUrl("https://velocitypowered.com"))
|
ClickEvent.openUrl("https://velocitypowered.com"))
|
||||||
.build())
|
.build())
|
||||||
.append(Component.text(" - "))
|
.append(Component.text(" - "))
|
||||||
.append(Component.text().content("GitHub")
|
.append(Component.text()
|
||||||
|
.content("GitHub")
|
||||||
.color(NamedTextColor.GREEN)
|
.color(NamedTextColor.GREEN)
|
||||||
.decoration(TextDecoration.UNDERLINED, true)
|
.decoration(TextDecoration.UNDERLINED, true)
|
||||||
.clickEvent(ClickEvent.openUrl(
|
.clickEvent(ClickEvent.openUrl(
|
||||||
@ -254,59 +188,48 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.build();
|
.build();
|
||||||
source.sendMessage(embellishment);
|
source.sendMessage(embellishment);
|
||||||
}
|
}
|
||||||
}
|
return Command.SINGLE_SUCCESS;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.info") != Tristate.FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Plugins implements SubCommand {
|
private record Plugins(ProxyServer server) implements Command<CommandSource> {
|
||||||
|
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
private Plugins(ProxyServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public int run(final CommandContext<CommandSource> context) {
|
||||||
if (args.length != 0) {
|
final CommandSource source = context.getSource();
|
||||||
source.sendMessage(Component.text("/velocity plugins", NamedTextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PluginContainer> plugins = ImmutableList.copyOf(server.getPluginManager().getPlugins());
|
final List<PluginContainer> plugins = List.copyOf(server.getPluginManager().getPlugins());
|
||||||
int pluginCount = plugins.size();
|
final int pluginCount = plugins.size();
|
||||||
|
|
||||||
if (pluginCount == 0) {
|
if (pluginCount == 0) {
|
||||||
source.sendMessage(Component.translatable("velocity.command.no-plugins",
|
source.sendMessage(Component.translatable("velocity.command.no-plugins",
|
||||||
NamedTextColor.YELLOW));
|
NamedTextColor.YELLOW));
|
||||||
return;
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextComponent.Builder listBuilder = Component.text();
|
final TextComponent.Builder listBuilder = Component.text();
|
||||||
for (int i = 0; i < pluginCount; i++) {
|
for (int i = 0; i < pluginCount; i++) {
|
||||||
PluginContainer plugin = plugins.get(i);
|
final PluginContainer plugin = plugins.get(i);
|
||||||
listBuilder.append(componentForPlugin(plugin.getDescription()));
|
listBuilder.append(componentForPlugin(plugin.getDescription()));
|
||||||
if (i + 1 < pluginCount) {
|
if (i + 1 < pluginCount) {
|
||||||
listBuilder.append(Component.text(", "));
|
listBuilder.append(Component.text(", "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslatableComponent.Builder output = Component.translatable()
|
final TranslatableComponent output = Component.translatable()
|
||||||
.key("velocity.command.plugins-list")
|
.key("velocity.command.plugins-list")
|
||||||
.color(NamedTextColor.YELLOW)
|
.color(NamedTextColor.YELLOW)
|
||||||
.args(listBuilder.build());
|
.arguments(listBuilder.build())
|
||||||
|
.build();
|
||||||
source.sendMessage(output);
|
source.sendMessage(output);
|
||||||
|
return Command.SINGLE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextComponent componentForPlugin(PluginDescription description) {
|
private TextComponent componentForPlugin(PluginDescription description) {
|
||||||
String pluginInfo = description.getName().orElse(description.getId())
|
final String pluginInfo = description.getName().orElse(description.getId())
|
||||||
+ description.getVersion().map(v -> " " + v).orElse("");
|
+ description.getVersion().map(v -> " " + v).orElse("");
|
||||||
|
|
||||||
TextComponent.Builder hoverText = Component.text().content(pluginInfo);
|
final TextComponent.Builder hoverText = Component.text().content(pluginInfo);
|
||||||
|
|
||||||
description.getUrl().ifPresent(url -> {
|
description.getUrl().ifPresent(url -> {
|
||||||
hoverText.append(Component.newline());
|
hoverText.append(Component.newline());
|
||||||
@ -333,61 +256,51 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
hoverText.append(Component.text(pdesc));
|
hoverText.append(Component.text(pdesc));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Component.text(description.getId(), NamedTextColor.GRAY)
|
return Component.text()
|
||||||
.hoverEvent(HoverEvent.showText(hoverText.build()));
|
.content(description.getId())
|
||||||
}
|
.color(NamedTextColor.GRAY)
|
||||||
|
.hoverEvent(HoverEvent.showText(hoverText.build()))
|
||||||
@Override
|
.build();
|
||||||
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Dump implements SubCommand {
|
private record Dump(ProxyServer server) implements Command<CommandSource> {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(Dump.class);
|
private static final Logger logger = LogManager.getLogger(Dump.class);
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
private Dump(ProxyServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public int run(final CommandContext<CommandSource> context) {
|
||||||
if (args.length != 0) {
|
final CommandSource source = context.getSource();
|
||||||
source.sendMessage(Component.text("/velocity dump", NamedTextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<RegisteredServer> allServers = ImmutableSet.copyOf(server.getAllServers());
|
final Collection<RegisteredServer> allServers = Set.copyOf(server.getAllServers());
|
||||||
JsonObject servers = new JsonObject();
|
final JsonObject servers = new JsonObject();
|
||||||
for (RegisteredServer iter : allServers) {
|
for (final RegisteredServer iter : allServers) {
|
||||||
servers.add(iter.getServerInfo().getName(),
|
servers.add(iter.getServerInfo().getName(),
|
||||||
InformationUtils.collectServerInfo(iter));
|
InformationUtils.collectServerInfo(iter));
|
||||||
}
|
}
|
||||||
JsonArray connectOrder = new JsonArray();
|
final JsonArray connectOrder = new JsonArray();
|
||||||
List<String> attemptedConnectionOrder = ImmutableList.copyOf(
|
final List<String> attemptedConnectionOrder = List.copyOf(
|
||||||
server.getConfiguration().getAttemptConnectionOrder());
|
server.getConfiguration().getAttemptConnectionOrder());
|
||||||
for (String s : attemptedConnectionOrder) {
|
for (final String s : attemptedConnectionOrder) {
|
||||||
connectOrder.add(s);
|
connectOrder.add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration());
|
final JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration());
|
||||||
proxyConfig.add("servers", servers);
|
proxyConfig.add("servers", servers);
|
||||||
proxyConfig.add("connectOrder", connectOrder);
|
proxyConfig.add("connectOrder", connectOrder);
|
||||||
proxyConfig.add("forcedHosts",
|
proxyConfig.add("forcedHosts",
|
||||||
InformationUtils.collectForcedHosts(server.getConfiguration()));
|
InformationUtils.collectForcedHosts(server.getConfiguration()));
|
||||||
|
|
||||||
JsonObject dump = new JsonObject();
|
final JsonObject dump = new JsonObject();
|
||||||
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion()));
|
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion()));
|
||||||
dump.add("platform", InformationUtils.collectEnvironmentInfo());
|
dump.add("platform", InformationUtils.collectEnvironmentInfo());
|
||||||
dump.add("config", proxyConfig);
|
dump.add("config", proxyConfig);
|
||||||
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
||||||
|
|
||||||
Path dumpPath = Path.of("velocity-dump-"
|
final Path dumpPath = Path.of("velocity-dump-"
|
||||||
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
|
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
|
||||||
+ ".json");
|
+ ".json");
|
||||||
try (BufferedWriter bw = Files.newBufferedWriter(
|
try (final BufferedWriter bw = Files.newBufferedWriter(
|
||||||
dumpPath, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW)) {
|
dumpPath, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW)) {
|
||||||
bw.write(InformationUtils.toHumanReadableString(dump));
|
bw.write(InformationUtils.toHumanReadableString(dump));
|
||||||
|
|
||||||
@ -397,32 +310,29 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
NamedTextColor.GREEN));
|
NamedTextColor.GREEN));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Failed to complete dump command, "
|
logger.error("Failed to complete dump command, "
|
||||||
+ "the executor was interrupted: " + e.getMessage());
|
+ "the executor was interrupted: " + e.getMessage(), e);
|
||||||
e.printStackTrace();
|
|
||||||
source.sendMessage(Component.text(
|
source.sendMessage(Component.text(
|
||||||
"We could not save the anonymized dump. Check the console for more details.",
|
"We could not save the anonymized dump. Check the console for more details.",
|
||||||
NamedTextColor.RED)
|
NamedTextColor.RED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
return Command.SINGLE_SUCCESS;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heap SubCommand.
|
* Heap SubCommand.
|
||||||
*/
|
*/
|
||||||
public static class Heap implements SubCommand {
|
public static final class Heap implements Command<CommandSource> {
|
||||||
private static final Logger logger = LogManager.getLogger(Heap.class);
|
private static final Logger logger = LogManager.getLogger(Heap.class);
|
||||||
private MethodHandle heapGenerator;
|
private MethodHandle heapGenerator;
|
||||||
private Consumer<CommandSource> heapConsumer;
|
private Consumer<CommandSource> heapConsumer;
|
||||||
private final Path dir = Path.of("./dumps");
|
private final Path dir = Path.of("./dumps");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public int run(final CommandContext<CommandSource> context) throws CommandSyntaxException {
|
||||||
|
final CommandSource source = context.getSource();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Files.notExists(dir)) {
|
if (Files.notExists(dir)) {
|
||||||
Files.createDirectories(dir);
|
Files.createDirectories(dir);
|
||||||
@ -480,39 +390,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
NamedTextColor.RED));
|
NamedTextColor.RED));
|
||||||
logger.error("Could not write heap", t);
|
logger.error("Could not write heap", t);
|
||||||
}
|
}
|
||||||
}
|
return Command.SINGLE_SUCCESS;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.heap") == Tristate.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback SubCommand.
|
|
||||||
*/
|
|
||||||
public static class Callback implements SubCommand {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
if (args.length != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UUID id;
|
|
||||||
try {
|
|
||||||
id = UUID.fromString(args[0]);
|
|
||||||
} catch (final IllegalArgumentException ignored) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClickCallbackManager.INSTANCE.runCallback(source, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
// Inject commands from the proxy.
|
// Inject commands from the proxy.
|
||||||
final CommandGraphInjector<CommandSource> injector = server.getCommandManager().getInjector();
|
final CommandGraphInjector<CommandSource> injector = server.getCommandManager().getInjector();
|
||||||
injector.inject(rootNode, serverConn.getPlayer());
|
injector.inject(rootNode, serverConn.getPlayer());
|
||||||
|
rootNode.removeChildByName("velocity:callback");
|
||||||
}
|
}
|
||||||
|
|
||||||
server.getEventManager().fire(
|
server.getEventManager().fire(
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren