13
0
geforkt von Mirrors/Velocity

Deprecate anonymous command registrations

Dieser Commit ist enthalten in:
Andrew Steinborn 2024-09-15 20:22:55 -04:00
Ursprung 4f227badc2
Commit 2016d1482f
15 geänderte Dateien mit 338 neuen und 37 gelöschten Zeilen

Datei anzeigen

@ -45,7 +45,9 @@ public interface CommandManager {
* @throws IllegalArgumentException if one of the given aliases is already registered, or
* the given command does not implement a registrable {@link Command} subinterface
* @see Command for a list of registrable Command subinterfaces
* @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified
*/
@Deprecated
default void register(String alias, Command command, String... otherAliases) {
register(metaBuilder(alias).aliases(otherAliases).build(), command);
}
@ -55,7 +57,9 @@ public interface CommandManager {
*
* @param command the command to register
* @throws IllegalArgumentException if the node alias is already registered
* @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified
*/
@Deprecated
void register(BrigadierCommand command);
/**

Datei anzeigen

@ -22,11 +22,13 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.api.command.BrigadierCommand;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyReloadEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
@ -52,6 +54,9 @@ import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.util.FaviconSerializer;
import com.velocitypowered.proxy.protocol.util.GameProfileSerializer;
@ -77,6 +82,7 @@ import java.nio.file.Path;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@ -111,6 +117,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/
public class VelocityServer implements ProxyServer, ForwardingAudience {
public static final String VELOCITY_URL = "https://velocitypowered.com";
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
public static final Gson GENERAL_GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE)
@ -163,7 +171,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
VelocityServer(final ProxyOptions options) {
pluginManager = new VelocityPluginManager(this);
eventManager = new VelocityEventManager(pluginManager);
commandManager = new VelocityCommandManager(eventManager);
commandManager = new VelocityCommandManager(eventManager, pluginManager);
scheduler = new VelocityScheduler(pluginManager);
console = new VelocityConsole(this);
cm = new ConnectionManager(this);
@ -200,6 +208,16 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
return new ProxyVersion(implName, implVendor, implVersion);
}
private VelocityPluginContainer createVirtualPlugin() {
ProxyVersion version = getVersion();
PluginDescription description = new VelocityPluginDescription(
"velocity", version.getName(), version.getVersion(), "The Velocity proxy",
VELOCITY_URL, ImmutableList.of(version.getVendor()), Collections.emptyList(), null);
VelocityPluginContainer container = new VelocityPluginContainer(description);
container.setInstance(VelocityVirtualPlugin.INSTANCE);
return container;
}
@Override
public VelocityCommandManager getCommandManager() {
return commandManager;
@ -214,6 +232,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
console.setupStreams();
pluginManager.registerPlugin(this.createVirtualPlugin());
registerTranslations();
@ -222,11 +241,35 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
cm.logChannelInformation();
// Initialize commands first
commandManager.register(VelocityCommand.create(this));
commandManager.register(CallbackCommand.create());
commandManager.register(ServerCommand.create(this));
commandManager.register("shutdown", ShutdownCommand.command(this),
"end", "stop");
final BrigadierCommand velocityParentCommand = VelocityCommand.create(this);
commandManager.register(
commandManager.metaBuilder(velocityParentCommand)
.plugin(VelocityVirtualPlugin.INSTANCE)
.build(),
velocityParentCommand
);
final BrigadierCommand callbackCommand = CallbackCommand.create();
commandManager.register(
commandManager.metaBuilder(callbackCommand)
.plugin(VelocityVirtualPlugin.INSTANCE)
.build(),
velocityParentCommand
);
final BrigadierCommand serverCommand = ServerCommand.create(this);
commandManager.register(
commandManager.metaBuilder(serverCommand)
.plugin(VelocityVirtualPlugin.INSTANCE)
.build(),
serverCommand
);
final BrigadierCommand shutdownCommand = ShutdownCommand.command(this);
commandManager.register(
commandManager.metaBuilder(shutdownCommand)
.plugin(VelocityVirtualPlugin.INSTANCE)
.aliases("end", "stop")
.build(),
shutdownCommand
);
new GlistCommand(this).register();
new SendCommand(this).register();

Datei anzeigen

@ -20,6 +20,7 @@ package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.MoreExecutors;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.Message;
import com.mojang.brigadier.ParseResults;
@ -37,11 +38,15 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.VelocityBrigadierMessage;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.event.command.PostCommandInvocationEvent;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import com.velocitypowered.proxy.command.registrar.BrigadierCommandRegistrar;
import com.velocitypowered.proxy.command.registrar.CommandRegistrar;
import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar;
import com.velocitypowered.proxy.command.registrar.SimpleCommandRegistrar;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -49,8 +54,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
@ -73,14 +77,16 @@ public class VelocityCommandManager implements CommandManager {
private final SuggestionsProvider<CommandSource> suggestionsProvider;
private final CommandGraphInjector<CommandSource> injector;
private final Map<String, CommandMeta> commandMetas;
private final ExecutorService asyncExecutor;
private final PluginManager pluginManager;
/**
* Constructs a command manager.
*
* @param eventManager the event manager
*/
public VelocityCommandManager(final VelocityEventManager eventManager) {
public VelocityCommandManager(final VelocityEventManager eventManager,
PluginManager pluginManager) {
this.pluginManager = pluginManager;
this.lock = new ReentrantReadWriteLock();
this.dispatcher = new CommandDispatcher<>();
this.eventManager = Preconditions.checkNotNull(eventManager);
@ -92,7 +98,6 @@ public class VelocityCommandManager implements CommandManager {
this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock());
this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock());
this.commandMetas = new ConcurrentHashMap<>();
this.asyncExecutor = ForkJoinPool.commonPool(); // TODO: remove entirely
}
public void setAnnounceProxyCommands(boolean announceProxyCommands) {
@ -222,16 +227,13 @@ public class VelocityCommandManager implements CommandManager {
return eventManager.fire(new CommandExecuteEvent(source, cmdLine));
}
private boolean executeImmediately0(final CommandSource source, final String cmdLine) {
private boolean executeImmediately0(final CommandSource source, final ParseResults<CommandSource> parsed) {
Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true);
CommandResult result = CommandResult.EXCEPTION;
try {
// The parse can fail if the requirement predicates throw
final ParseResults<CommandSource> parse = this.parse(normalizedInput, source);
boolean executed = dispatcher.execute(parse) != BrigadierCommand.FORWARD;
boolean executed = dispatcher.execute(parsed) != BrigadierCommand.FORWARD;
result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED;
return executed;
} catch (final CommandSyntaxException e) {
@ -253,9 +255,9 @@ public class VelocityCommandManager implements CommandManager {
}
} catch (final Throwable e) {
// Ugly, ugly swallowing of everything Throwable, because plugins are naughty.
throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + source, e);
throw new RuntimeException("Unable to invoke command " + parsed.getReader().getString() + "for " + source, e);
} finally {
eventManager.fireAndForget(new PostCommandInvocationEvent(source, cmdLine, result));
eventManager.fireAndForget(new PostCommandInvocationEvent(source, parsed.getReader().getString(), result));
}
}
@ -264,13 +266,17 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
return callCommandEvent(source, cmdLine).thenApplyAsync(event -> {
return callCommandEvent(source, cmdLine).thenComposeAsync(event -> {
CommandExecuteEvent.CommandResult commandResult = event.getResult();
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
return false;
return CompletableFuture.completedFuture(false);
}
return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand()));
}, asyncExecutor);
final ParseResults<CommandSource> parsed = this.parse(
commandResult.getCommand().orElse(cmdLine), source);
return CompletableFuture.supplyAsync(
() -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed)
);
}, figureAsyncExecutorForParsing());
}
@Override
@ -279,7 +285,13 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
return CompletableFuture.supplyAsync(() -> executeImmediately0(source, cmdLine), asyncExecutor);
return CompletableFuture.supplyAsync(
() -> this.parse(cmdLine, source), figureAsyncExecutorForParsing()
).thenCompose(
parsed -> CompletableFuture.supplyAsync(
() -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed)
)
);
}
/**
@ -327,9 +339,10 @@ public class VelocityCommandManager implements CommandManager {
* @return the parse results
*/
private ParseResults<CommandSource> parse(final String input, final CommandSource source) {
final String normalizedInput = VelocityCommands.normalizeInput(input, true);
lock.readLock().lock();
try {
return dispatcher.parse(input, source);
return dispatcher.parse(normalizedInput, source);
} finally {
lock.readLock().unlock();
}
@ -373,4 +386,25 @@ public class VelocityCommandManager implements CommandManager {
public CommandGraphInjector<CommandSource> getInjector() {
return injector;
}
private Executor getAsyncExecutor(ParseResults<CommandSource> parse) {
Object registrant;
if (parse.getContext().getCommand() instanceof VelocityBrigadierCommandWrapper vbcw) {
registrant = vbcw.registrant() == null ? VelocityVirtualPlugin.INSTANCE : vbcw.registrant();
} else {
registrant = VelocityVirtualPlugin.INSTANCE;
}
return pluginManager.ensurePluginContainer(registrant).getExecutorService();
}
private Executor figureAsyncExecutorForParsing() {
final Thread thread = Thread.currentThread();
if (thread instanceof FastThreadLocalThread) {
// we *never* want to block the Netty event loop, so use the async executor
return pluginManager.ensurePluginContainer(VelocityVirtualPlugin.INSTANCE).getExecutorService();
} else {
// it's some other thread that isn't a Netty event loop thread. direct execution it is!
return MoreExecutors.directExecutor();
}
}
}

Datei anzeigen

@ -24,6 +24,7 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.context.ParsedCommandNode;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
@ -32,6 +33,7 @@ import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.InvocableCommand;
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -44,6 +46,59 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/
public final class VelocityCommands {
// Wrapping
/**
* Walks the command node tree and wraps all {@link Command} instances in a {@link VelocityBrigadierCommandWrapper},
* to indicate the plugin that registered the command. This also has the side effect of cloning
* the command node tree.
*
* @param delegate the command node to wrap
* @param registrant the plugin that registered the command
* @return the wrapped command node
*/
public static CommandNode<CommandSource> wrap(final CommandNode<CommandSource> delegate,
final @Nullable Object registrant) {
Preconditions.checkNotNull(delegate, "delegate");
if (registrant == null) {
// the registrant is null if the `plugin` was absent when we try to register the command
return delegate;
}
com.mojang.brigadier.Command<CommandSource> maybeCommand = delegate.getCommand();
if (maybeCommand != null && !(maybeCommand instanceof VelocityBrigadierCommandWrapper)) {
maybeCommand = VelocityBrigadierCommandWrapper.wrap(delegate.getCommand(), registrant);
}
if (delegate instanceof LiteralCommandNode<CommandSource> lcn) {
var literalBuilder = shallowCopyAsBuilder(lcn, delegate.getName(), true);
literalBuilder.executes(maybeCommand);
// we also need to wrap any children
for (final CommandNode<CommandSource> child : delegate.getChildren()) {
literalBuilder.then(wrap(child, registrant));
}
if (delegate.getRedirect() != null) {
literalBuilder.redirect(wrap(delegate.getRedirect(), registrant));
}
return literalBuilder.build();
} else if (delegate instanceof VelocityArgumentCommandNode<CommandSource, ?> vacn) {
return vacn.withCommand(maybeCommand)
.withRedirect(delegate.getRedirect() != null ? wrap(delegate.getRedirect(), registrant) : null);
} else if (delegate instanceof ArgumentCommandNode) {
var argBuilder = delegate.createBuilder().executes(maybeCommand);
// we also need to wrap any children
for (final CommandNode<CommandSource> child : delegate.getChildren()) {
argBuilder.then(wrap(child, registrant));
}
if (delegate.getRedirect() != null) {
argBuilder.redirect(wrap(delegate.getRedirect(), registrant));
}
return argBuilder.build();
} else {
throw new IllegalArgumentException("Unsupported node type: " + delegate.getClass());
}
}
// Normalization
/**
@ -135,6 +190,33 @@ public final class VelocityCommands {
*/
public static LiteralCommandNode<CommandSource> shallowCopy(
final LiteralCommandNode<CommandSource> original, final String newName) {
return shallowCopy(original, newName, original.getCommand());
}
/**
* Creates a copy of the given literal with the specified name.
*
* @param original the literal node to copy
* @param newName the name of the returned literal node
* @param newCommand the new command to set on the copied node
* @return a copy of the literal with the given name
*/
private static LiteralCommandNode<CommandSource> shallowCopy(
final LiteralCommandNode<CommandSource> original, final String newName,
final com.mojang.brigadier.Command<CommandSource> newCommand) {
return shallowCopyAsBuilder(original, newName, false).executes(newCommand).build();
}
/**
* Creates a copy of the given literal with the specified name.
*
* @param original the literal node to copy
* @param newName the name of the returned literal node
* @return a copy of the literal with the given name
*/
private static LiteralArgumentBuilder<CommandSource> shallowCopyAsBuilder(
final LiteralCommandNode<CommandSource> original, final String newName,
final boolean skipChildren) {
// Brigadier resolves the redirect of a node if further input can be parsed.
// Let <bar> be a literal node having a redirect to a <foo> literal. Then,
// the context returned by CommandDispatcher#parseNodes when given the input
@ -150,10 +232,12 @@ public final class VelocityCommands {
.requiresWithContext(original.getContextRequirement())
.forward(original.getRedirect(), original.getRedirectModifier(), original.isFork())
.executes(original.getCommand());
if (!skipChildren) {
for (final CommandNode<CommandSource> child : original.getChildren()) {
builder.then(child);
}
return builder.build();
}
return builder;
}
// Arguments node

Datei anzeigen

@ -93,6 +93,16 @@ public class VelocityArgumentCommandNode<S, T> extends ArgumentCommandNode<S, St
throw new UnsupportedOperationException();
}
public VelocityArgumentCommandNode<S, T> withCommand(Command<S> command) {
return new VelocityArgumentCommandNode<>(getName(), type, command, getRequirement(),
getContextRequirement(), getRedirect(), getRedirectModifier(), isFork(), getCustomSuggestions());
}
public VelocityArgumentCommandNode<S, T> withRedirect(CommandNode<S> target) {
return new VelocityArgumentCommandNode<>(getName(), type, getCommand(), getRequirement(),
getContextRequirement(), target, getRedirectModifier(), isFork(), getCustomSuggestions());
}
@Override
public boolean isValidInput(final String input) {
return true;

Datei anzeigen

@ -0,0 +1,67 @@
/*
* 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.brigadier;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.velocitypowered.api.command.CommandSource;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Wraps a Brigadier command to allow us to track the registrant.
*/
public class VelocityBrigadierCommandWrapper implements Command<CommandSource> {
private final Command<CommandSource> delegate;
private final Object registrant;
private VelocityBrigadierCommandWrapper(Command<CommandSource> delegate, Object registrant) {
this.delegate = delegate;
this.registrant = registrant;
}
/**
* Transforms the given command into a {@code VelocityBrigadierCommandWrapper} if the registrant
* is not null and if the command is not already wrapped.
*
* @param delegate the command to wrap
* @param registrant the registrant of the command
* @return the wrapped command, if necessary
*/
public static Command<CommandSource> wrap(Command<CommandSource> delegate, @Nullable Object registrant) {
if (registrant == null) {
// nothing to wrap
return delegate;
}
if (delegate instanceof VelocityBrigadierCommandWrapper) {
// already wrapped
return delegate;
}
return new VelocityBrigadierCommandWrapper(delegate, registrant);
}
@Override
public int run(CommandContext<CommandSource> context) throws CommandSyntaxException {
return delegate.run(context);
}
public Object registrant() {
return registrant;
}
}

Datei anzeigen

@ -31,6 +31,7 @@ import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.util.List;
import java.util.Optional;
import net.kyori.adventure.text.Component;
@ -80,7 +81,13 @@ public class GlistCommand {
.executes(this::serverCount)
.build();
rootNode.then(serverNode);
server.getCommandManager().register(new BrigadierCommand(rootNode));
final BrigadierCommand command = new BrigadierCommand(rootNode);
server.getCommandManager().register(
server.getCommandManager().metaBuilder(command)
.plugin(VelocityVirtualPlugin.INSTANCE)
.build(),
command
);
}
private int totalCount(final CommandContext<CommandSource> context) {

Datei anzeigen

@ -30,6 +30,7 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.util.Objects;
import java.util.Optional;
import net.kyori.adventure.text.Component;
@ -96,7 +97,13 @@ public class SendCommand {
.build();
playerNode.then(serverNode);
rootNode.then(playerNode.build());
server.getCommandManager().register(new BrigadierCommand(rootNode.build()));
final BrigadierCommand command = new BrigadierCommand(rootNode);
server.getCommandManager().register(
server.getCommandManager().metaBuilder(command)
.plugin(VelocityVirtualPlugin.INSTANCE)
.build(),
command
);
}
private int usage(final CommandContext<CommandSource> context) {

Datei anzeigen

@ -40,17 +40,19 @@ public final class BrigadierCommandRegistrar extends AbstractCommandRegistrar<Br
// Register it (if valid), since it's probably what the user expects.
// If invalid, the metadata contains the same alias, but in lowercase.
final LiteralCommandNode<CommandSource> literal = command.getNode();
final LiteralCommandNode<CommandSource> wrapped =
(LiteralCommandNode<CommandSource>) VelocityCommands.wrap(literal, meta.getPlugin());
final String primaryAlias = literal.getName();
if (VelocityCommands.isValidAlias(primaryAlias)) {
// Register directly without copying
this.register(literal);
this.register(wrapped);
}
for (final String alias : meta.getAliases()) {
if (primaryAlias.equals(alias)) {
continue;
}
this.register(literal, alias);
this.register(wrapped, alias);
}
// Brigadier commands don't support hinting, ignore

Datei anzeigen

@ -32,6 +32,7 @@ import com.velocitypowered.api.command.InvocableCommand;
import com.velocitypowered.proxy.command.VelocityCommandMeta;
import com.velocitypowered.proxy.command.VelocityCommands;
import com.velocitypowered.proxy.command.brigadier.VelocityArgumentBuilder;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import com.velocitypowered.proxy.command.invocation.CommandInvocationFactory;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
@ -76,11 +77,11 @@ abstract class InvocableCommandRegistrar<T extends InvocableCommand<I>,
final I invocation = invocationFactory.create(context);
return command.hasPermission(invocation);
};
final Command<CommandSource> callback = context -> {
final Command<CommandSource> callback = VelocityBrigadierCommandWrapper.wrap(context -> {
final I invocation = invocationFactory.create(context);
command.execute(invocation);
return 1; // handled
};
}, meta.getPlugin());
final LiteralCommandNode<CommandSource> literal = LiteralArgumentBuilder
.<CommandSource>literal(alias)

Datei anzeigen

@ -68,7 +68,12 @@ public class VelocityPluginManager implements PluginManager {
this.server = checkNotNull(server, "server");
}
private void registerPlugin(PluginContainer plugin) {
/**
* Registers a plugin with the plugin manager.
*
* @param plugin the plugin to register
*/
public void registerPlugin(PluginContainer plugin) {
pluginsById.put(plugin.getDescription().getId(), plugin);
Optional<?> instance = plugin.getInstance();
instance.ifPresent(o -> pluginInstances.put(o, plugin));

Datei anzeigen

@ -0,0 +1,29 @@
/*
* 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.plugin.virtual;
/**
* A singleton plugin object that represents the Velocity proxy itself.
*/
public class VelocityVirtualPlugin {
@SuppressWarnings("InstantiationOfUtilityClass")
public static final VelocityVirtualPlugin INSTANCE = new VelocityVirtualPlugin();
private VelocityVirtualPlugin() {
}
}

Datei anzeigen

@ -230,7 +230,7 @@ public class BrigadierCommandTests extends CommandTestSuite {
final Exception wrapper = assertThrows(CompletionException.class, () ->
manager.executeAsync(source, "hello").join());
assertSame(expected, wrapper.getCause().getCause());
assertSame(expected, wrapper.getCause());
}
// Suggestions

Datei anzeigen

@ -29,6 +29,7 @@ import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.event.MockEventManager;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.testutil.FakePluginManager;
import java.util.Arrays;
import java.util.Collection;
import org.junit.jupiter.api.BeforeAll;
@ -48,7 +49,7 @@ abstract class CommandTestSuite {
@BeforeEach
void setUp() {
this.manager = new VelocityCommandManager(eventManager);
this.manager = new VelocityCommandManager(eventManager, new FakePluginManager());
}
final void assertHandled(final String input) {

Datei anzeigen

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
@ -39,6 +40,8 @@ public class FakePluginManager implements PluginManager {
private final PluginContainer containerA = new FakePluginContainer("a", PLUGIN_A);
private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B);
private final PluginContainer containerVelocity = new FakePluginContainer("velocity",
VelocityVirtualPlugin.INSTANCE);
private ExecutorService service = Executors.newCachedThreadPool(
new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build()
@ -50,6 +53,8 @@ public class FakePluginManager implements PluginManager {
return Optional.of(containerA);
} else if (instance == PLUGIN_B) {
return Optional.of(containerB);
} else if (instance == VelocityVirtualPlugin.INSTANCE) {
return Optional.of(containerVelocity);
} else {
return Optional.empty();
}
@ -62,6 +67,8 @@ public class FakePluginManager implements PluginManager {
return Optional.of(containerA);
case "b":
return Optional.of(containerB);
case "velocity":
return Optional.of(containerVelocity);
default:
return Optional.empty();
}
@ -69,7 +76,7 @@ public class FakePluginManager implements PluginManager {
@Override
public @NonNull Collection<PluginContainer> getPlugins() {
return ImmutableList.of(containerA, containerB);
return ImmutableList.of(containerVelocity, containerA, containerB);
}
@Override