Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-24 15:20:35 +01:00
Merge pull request #299 from Leymooo/command-event
Add event for commands
Dieser Commit ist enthalten in:
Commit
e759668f53
@ -1,5 +1,7 @@
|
|||||||
package com.velocitypowered.api.command;
|
package com.velocitypowered.api.command;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an interface to register a command executor with the proxy.
|
* Represents an interface to register a command executor with the proxy.
|
||||||
*/
|
*/
|
||||||
@ -34,11 +36,52 @@ public interface CommandManager {
|
|||||||
void unregister(String alias);
|
void unregister(String alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to execute a command from the specified {@code cmdLine}.
|
* Calls CommandExecuteEvent and attempts to execute a command using the specified {@code cmdLine}
|
||||||
|
* in a blocking fashion.
|
||||||
*
|
*
|
||||||
* @param source the command's source
|
* @param source the command's source
|
||||||
* @param cmdLine the command to run
|
* @param cmdLine the command to run
|
||||||
* @return true if the command was found and executed, false if it was not
|
* @return true if the command was found and executed, false if it was not
|
||||||
|
*
|
||||||
|
* @deprecated This method will block current thread during event call and command execution.
|
||||||
|
* Prefer {@link #executeAsync(CommandSource, String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean execute(CommandSource source, String cmdLine);
|
boolean execute(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to execute a command using the specified {@code cmdLine} in a blocking fashion without
|
||||||
|
* calling CommandExecuteEvent.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return true if the command was found and executed, false if it was not
|
||||||
|
*
|
||||||
|
* @deprecated This method will block current thread during event and command execution.
|
||||||
|
* Prefer {@link #executeImmediatelyAsync(CommandSource, String)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
boolean executeImmediately(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls CommandExecuteEvent and attempts to execute a command from the specified {@code cmdLine}
|
||||||
|
* async.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return A future that will be completed with the result of the command execution.
|
||||||
|
* Can be completed exceptionally if exception was thrown during execution.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Boolean> executeAsync(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to execute a command from the specified {@code cmdLine} async
|
||||||
|
* without calling CommandExecuteEvent.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return A future that will be completed with the result of the command execution.
|
||||||
|
* Can be completed exceptionally if exception was thrown during execution.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package com.velocitypowered.api.event.command;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired when a player types in a chat message.
|
||||||
|
*/
|
||||||
|
public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||||
|
|
||||||
|
private final CommandSource commandSource;
|
||||||
|
private final String command;
|
||||||
|
private CommandResult result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a CommandExecuteEvent.
|
||||||
|
* @param commandSource the source executing the command
|
||||||
|
* @param command the command being executed without first slash
|
||||||
|
*/
|
||||||
|
public CommandExecuteEvent(CommandSource commandSource, String command) {
|
||||||
|
this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource");
|
||||||
|
this.command = Preconditions.checkNotNull(command, "command");
|
||||||
|
this.result = CommandResult.allowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSource getCommandSource() {
|
||||||
|
return commandSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the original command being executed without first slash.
|
||||||
|
* @return the original command being executed
|
||||||
|
*/
|
||||||
|
public String getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResult(CommandResult result) {
|
||||||
|
this.result = Preconditions.checkNotNull(result, "result");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CommandExecuteEvent{"
|
||||||
|
+ "commandSource=" + commandSource
|
||||||
|
+ ", command=" + command
|
||||||
|
+ ", result=" + result
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of the {@link CommandExecuteEvent}.
|
||||||
|
*/
|
||||||
|
public static final class CommandResult implements Result {
|
||||||
|
|
||||||
|
private static final CommandResult ALLOWED = new CommandResult(true, false,null);
|
||||||
|
private static final CommandResult DENIED = new CommandResult(false, false,null);
|
||||||
|
private static final CommandResult FORWARD_TO_SERVER = new CommandResult(false, true, null);
|
||||||
|
|
||||||
|
private @Nullable String command;
|
||||||
|
private final boolean status;
|
||||||
|
private final boolean forward;
|
||||||
|
|
||||||
|
private CommandResult(boolean status, boolean forward, @Nullable String command) {
|
||||||
|
this.status = status;
|
||||||
|
this.forward = forward;
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getCommand() {
|
||||||
|
return Optional.ofNullable(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForwardToServer() {
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return status ? "allowed" : "denied";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the command to be sent, without modification.
|
||||||
|
* @return the allowed result
|
||||||
|
*/
|
||||||
|
public static CommandResult allowed() {
|
||||||
|
return ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed.
|
||||||
|
* @return the denied result
|
||||||
|
*/
|
||||||
|
public static CommandResult denied() {
|
||||||
|
return DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed, but forward command to server.
|
||||||
|
* @return the forward result
|
||||||
|
*/
|
||||||
|
public static CommandResult forwardToServer() {
|
||||||
|
return FORWARD_TO_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed on proxy, but forward command to server.
|
||||||
|
* @param newCommand the command without first slash to use instead
|
||||||
|
* @return a result with a new command being forwarded to server
|
||||||
|
*/
|
||||||
|
public static CommandResult forwardToServer(@NonNull String newCommand) {
|
||||||
|
Preconditions.checkNotNull(newCommand, "newCommand");
|
||||||
|
return new CommandResult(false, true, newCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the command to be executed, but silently replaced old command with another.
|
||||||
|
* @param newCommand the command to use instead without first slash
|
||||||
|
* @return a result with a new command
|
||||||
|
*/
|
||||||
|
public static CommandResult command(@NonNull String newCommand) {
|
||||||
|
Preconditions.checkNotNull(newCommand, "newCommand");
|
||||||
|
return new CommandResult(true, false, newCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,10 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getMessage() {
|
||||||
|
return Optional.ofNullable(message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAllowed() {
|
public boolean isAllowed() {
|
||||||
return status;
|
return status;
|
||||||
@ -96,10 +100,6 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
return DENIED;
|
return DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getMessage() {
|
|
||||||
return Optional.ofNullable(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the message to be sent, but silently replaced with another.
|
* Allows the message to be sent, but silently replaced with another.
|
||||||
* @param message the message to use instead
|
* @param message the message to use instead
|
||||||
@ -110,6 +110,4 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
return new ChatResult(true, message);
|
return new ChatResult(true, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
private @MonotonicNonNull VelocityConfiguration configuration;
|
private @MonotonicNonNull VelocityConfiguration configuration;
|
||||||
private @MonotonicNonNull KeyPair serverKeyPair;
|
private @MonotonicNonNull KeyPair serverKeyPair;
|
||||||
private final ServerMap servers;
|
private final ServerMap servers;
|
||||||
private final VelocityCommandManager commandManager = new VelocityCommandManager();
|
private final VelocityCommandManager commandManager;
|
||||||
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
private final VelocityPluginManager pluginManager;
|
private final VelocityPluginManager pluginManager;
|
||||||
@ -111,6 +111,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
VelocityServer(final ProxyOptions options) {
|
VelocityServer(final ProxyOptions options) {
|
||||||
pluginManager = new VelocityPluginManager(this);
|
pluginManager = new VelocityPluginManager(this);
|
||||||
eventManager = new VelocityEventManager(pluginManager);
|
eventManager = new VelocityEventManager(pluginManager);
|
||||||
|
commandManager = new VelocityCommandManager(eventManager);
|
||||||
scheduler = new VelocityScheduler(pluginManager);
|
scheduler = new VelocityScheduler(pluginManager);
|
||||||
console = new VelocityConsole(this);
|
console = new VelocityConsole(this);
|
||||||
cm = new ConnectionManager(this);
|
cm = new ConnectionManager(this);
|
||||||
|
@ -7,16 +7,26 @@ import com.velocitypowered.api.command.Command;
|
|||||||
import com.velocitypowered.api.command.CommandManager;
|
import com.velocitypowered.api.command.CommandManager;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.RawCommand;
|
import com.velocitypowered.api.command.RawCommand;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||||
|
import com.velocitypowered.proxy.plugin.VelocityEventManager;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class VelocityCommandManager implements CommandManager {
|
public class VelocityCommandManager implements CommandManager {
|
||||||
|
|
||||||
private final Map<String, RawCommand> commands = new HashMap<>();
|
private final Map<String, RawCommand> commands = new HashMap<>();
|
||||||
|
private final VelocityEventManager eventManager;
|
||||||
|
|
||||||
|
public VelocityCommandManager(VelocityEventManager eventManager) {
|
||||||
|
this.eventManager = eventManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -47,9 +57,36 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
|
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls CommandExecuteEvent.
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmd the command
|
||||||
|
* @return CompletableFuture of event
|
||||||
|
*/
|
||||||
|
public CompletableFuture<CommandExecuteEvent> callCommandEvent(CommandSource source, String cmd) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmd, "cmd");
|
||||||
|
return eventManager.fire(new CommandExecuteEvent(source, cmd));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(CommandSource source, String cmdLine) {
|
public boolean execute(CommandSource source, String cmdLine) {
|
||||||
Preconditions.checkNotNull(source, "invoker");
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
|
CommandExecuteEvent event = callCommandEvent(source, cmdLine).join();
|
||||||
|
CommandResult commandResult = event.getResult();
|
||||||
|
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cmdLine = commandResult.getCommand().orElse(event.getCommand());
|
||||||
|
|
||||||
|
return executeImmediately(source, cmdLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean executeImmediately(CommandSource source, String cmdLine) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
String alias = cmdLine;
|
String alias = cmdLine;
|
||||||
@ -75,6 +112,40 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Boolean> executeAsync(CommandSource source, String cmdLine) {
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
callCommandEvent(source, cmdLine).thenAccept(event -> {
|
||||||
|
CommandResult commandResult = event.getResult();
|
||||||
|
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||||
|
result.complete(false);
|
||||||
|
}
|
||||||
|
String command = commandResult.getCommand().orElse(event.getCommand());
|
||||||
|
try {
|
||||||
|
result.complete(executeImmediately(source, command));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
eventManager.getService().execute(() -> {
|
||||||
|
try {
|
||||||
|
result.complete(executeImmediately(source, cmdLine));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasCommand(String command) {
|
public boolean hasCommand(String command) {
|
||||||
return commands.containsKey(command);
|
return commands.containsKey(command);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
|||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
||||||
import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket;
|
import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
||||||
@ -123,17 +124,30 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
String msg = packet.getMessage();
|
String msg = packet.getMessage();
|
||||||
if (msg.startsWith("/")) {
|
if (msg.startsWith("/")) {
|
||||||
try {
|
|
||||||
if (!server.getCommandManager().execute(player, msg.substring(1))) {
|
server.getCommandManager().callCommandEvent(player, msg.substring(1))
|
||||||
return false;
|
.thenAcceptAsync(event -> {
|
||||||
}
|
CommandExecuteEvent.CommandResult commandResult = event.getResult();
|
||||||
} catch (Exception e) {
|
Optional<String> eventCommand = event.getResult().getCommand();
|
||||||
logger.info("Exception occurred while running command for {}", player.getUsername(),
|
String command = eventCommand.orElse(event.getCommand());
|
||||||
e);
|
if (commandResult.isForwardToServer()) {
|
||||||
player.sendMessage(
|
smc.write(Chat.createServerbound(command));
|
||||||
TextComponent.of("An error occurred while running this command.", TextColor.RED));
|
return;
|
||||||
return true;
|
}
|
||||||
}
|
if (commandResult.isAllowed()) {
|
||||||
|
try {
|
||||||
|
if (!server.getCommandManager().executeImmediately(player, command)) {
|
||||||
|
smc.write(Chat.createServerbound(command));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info("Exception occurred while running command for {}", player.getUsername(),
|
||||||
|
e);
|
||||||
|
player.sendMessage(
|
||||||
|
TextComponent.of("An error occurred while running this command.",
|
||||||
|
TextColor.RED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, smc.eventLoop());
|
||||||
} else {
|
} else {
|
||||||
PlayerChatEvent event = new PlayerChatEvent(player, msg);
|
PlayerChatEvent event = new PlayerChatEvent(player, msg);
|
||||||
server.getEventManager().fire(event)
|
server.getEventManager().fire(event)
|
||||||
|
@ -8,6 +8,8 @@ import com.velocitypowered.api.permission.Tristate;
|
|||||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
|
@ -185,6 +185,10 @@ public class VelocityEventManager implements EventManager {
|
|||||||
fireEvent(new ProxyShutdownEvent());
|
fireEvent(new ProxyShutdownEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExecutorService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
private static class VelocityMethodScanner implements MethodScanner<Object> {
|
private static class VelocityMethodScanner implements MethodScanner<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren