geforkt von Mirrors/Velocity
Add console tab complete, shutdown command, gracefully kick players.
Dieser Commit ist enthalten in:
Ursprung
beb947cc3e
Commit
bb601dca4b
@ -10,6 +10,7 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import com.velocitypowered.network.ConnectionManager;
|
import com.velocitypowered.network.ConnectionManager;
|
||||||
import com.velocitypowered.proxy.command.ServerCommand;
|
import com.velocitypowered.proxy.command.ServerCommand;
|
||||||
|
import com.velocitypowered.proxy.command.ShutdownCommand;
|
||||||
import com.velocitypowered.proxy.command.VelocityCommand;
|
import com.velocitypowered.proxy.command.VelocityCommand;
|
||||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
@ -21,6 +22,7 @@ import com.velocitypowered.proxy.util.EncryptionUtils;
|
|||||||
import com.velocitypowered.proxy.util.ServerMap;
|
import com.velocitypowered.proxy.util.ServerMap;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.serializer.ComponentSerializers;
|
import net.kyori.text.serializer.ComponentSerializers;
|
||||||
import net.kyori.text.serializer.GsonComponentSerializer;
|
import net.kyori.text.serializer.GsonComponentSerializer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@ -50,7 +52,8 @@ public class VelocityServer implements ProxyServer {
|
|||||||
private KeyPair serverKeyPair;
|
private KeyPair serverKeyPair;
|
||||||
private final ServerMap servers = new ServerMap();
|
private final ServerMap servers = new ServerMap();
|
||||||
private final CommandManager commandManager = new CommandManager();
|
private final CommandManager commandManager = new CommandManager();
|
||||||
private final AtomicBoolean shutdown = new AtomicBoolean(false);
|
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
||||||
|
private boolean shutdown = false;
|
||||||
|
|
||||||
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
||||||
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
||||||
@ -69,6 +72,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
private VelocityServer() {
|
private VelocityServer() {
|
||||||
commandManager.registerCommand("velocity", new VelocityCommand());
|
commandManager.registerCommand("velocity", new VelocityCommand());
|
||||||
commandManager.registerCommand("server", new ServerCommand());
|
commandManager.registerCommand("server", new ServerCommand());
|
||||||
|
commandManager.registerCommand("shutdown", new ShutdownCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VelocityServer getServer() {
|
public static VelocityServer getServer() {
|
||||||
@ -136,13 +140,19 @@ public class VelocityServer implements ProxyServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShutdown() {
|
public boolean isShutdown() {
|
||||||
return shutdown.get();
|
return shutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (!shutdown.compareAndSet(false, true)) return;
|
if (!shutdownInProgress.compareAndSet(false, true)) return;
|
||||||
logger.info("Shutting down the proxy...");
|
logger.info("Shutting down the proxy...");
|
||||||
|
|
||||||
|
for (ConnectedPlayer player : ImmutableList.copyOf(connectionsByUuid.values())) {
|
||||||
|
player.close(TextComponent.of("Proxy shutting down."));
|
||||||
|
}
|
||||||
|
|
||||||
this.cm.shutdown();
|
this.cm.shutdown();
|
||||||
|
shutdown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NettyHttpClient getHttpClient() {
|
public NettyHttpClient getHttpClient() {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package com.velocitypowered.proxy.command;
|
package com.velocitypowered.proxy.command;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.command.CommandExecutor;
|
import com.velocitypowered.api.command.CommandExecutor;
|
||||||
import com.velocitypowered.api.command.CommandInvoker;
|
import com.velocitypowered.api.command.CommandInvoker;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CommandManager {
|
public class CommandManager {
|
||||||
private final Map<String, CommandExecutor> executors = new HashMap<>();
|
private final Map<String, CommandExecutor> executors = new HashMap<>();
|
||||||
@ -45,4 +48,33 @@ public class CommandManager {
|
|||||||
throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + invoker, e);
|
throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + invoker, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> offerSuggestions(CommandInvoker invoker, String cmdLine) {
|
||||||
|
Preconditions.checkNotNull(invoker, "invoker");
|
||||||
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
|
String[] split = cmdLine.split(" ", -1);
|
||||||
|
if (split.length == 0) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
String command = split[0];
|
||||||
|
if (split.length == 1) {
|
||||||
|
return executors.keySet().stream()
|
||||||
|
.filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
||||||
|
CommandExecutor executor = executors.get(command);
|
||||||
|
if (executor == null) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return executor.suggest(invoker, actualArgs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to invoke suggestions for command " + command + " for " + invoker, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.velocitypowered.proxy.command;
|
package com.velocitypowered.proxy.command;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.command.CommandExecutor;
|
import com.velocitypowered.api.command.CommandExecutor;
|
||||||
import com.velocitypowered.api.command.CommandInvoker;
|
import com.velocitypowered.api.command.CommandInvoker;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
@ -9,6 +10,7 @@ import net.kyori.text.TextComponent;
|
|||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -38,4 +40,20 @@ public class ServerCommand implements CommandExecutor {
|
|||||||
player.sendMessage(TextComponent.of("Available servers: " + serverList, TextColor.YELLOW));
|
player.sendMessage(TextComponent.of("Available servers: " + serverList, TextColor.YELLOW));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggest(@Nonnull CommandInvoker invoker, @Nonnull String[] currentArgs) {
|
||||||
|
if (currentArgs.length == 0) {
|
||||||
|
return VelocityServer.getServer().getAllServers().stream()
|
||||||
|
.map(ServerInfo::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else if (currentArgs.length == 1) {
|
||||||
|
return VelocityServer.getServer().getAllServers().stream()
|
||||||
|
.map(ServerInfo::getName)
|
||||||
|
.filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.velocitypowered.proxy.command;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.command.CommandExecutor;
|
||||||
|
import com.velocitypowered.api.command.CommandInvoker;
|
||||||
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import net.kyori.text.TextComponent;
|
||||||
|
import net.kyori.text.format.TextColor;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
public class ShutdownCommand implements CommandExecutor {
|
||||||
|
@Override
|
||||||
|
public void execute(@Nonnull CommandInvoker invoker, @Nonnull String[] args) {
|
||||||
|
if (invoker != VelocityServer.getServer().getConsoleCommandInvoker()) {
|
||||||
|
invoker.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VelocityServer.getServer().shutdown();
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ public class VelocityCommand implements CommandExecutor {
|
|||||||
TextComponent thisIsVelocity = TextComponent.builder()
|
TextComponent thisIsVelocity = TextComponent.builder()
|
||||||
.content("This is ")
|
.content("This is ")
|
||||||
.append(TextComponent.of("Velocity " + implVersion, TextColor.DARK_AQUA))
|
.append(TextComponent.of("Velocity " + implVersion, TextColor.DARK_AQUA))
|
||||||
.append(TextComponent.of(", the next generation Minecraft: Java Edition proxy.", TextColor.WHITE))
|
.append(TextComponent.of(", the next generation Minecraft: Java Edition proxy.").resetStyle())
|
||||||
.build();
|
.build();
|
||||||
TextComponent velocityInfo = TextComponent.builder()
|
TextComponent velocityInfo = TextComponent.builder()
|
||||||
.content("Copyright 2018 Velocity Contributors. Velocity is freely licensed under the terms of the " +
|
.content("Copyright 2018 Velocity Contributors. Velocity is freely licensed under the terms of the " +
|
||||||
@ -28,7 +28,7 @@ public class VelocityCommand implements CommandExecutor {
|
|||||||
.color(TextColor.GREEN)
|
.color(TextColor.GREEN)
|
||||||
.clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.velocitypowered.com"))
|
.clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.velocitypowered.com"))
|
||||||
.build())
|
.build())
|
||||||
.append(TextComponent.of(" or the ", TextColor.WHITE))
|
.append(TextComponent.of(" or the ").resetStyle())
|
||||||
.append(TextComponent.builder("Velocity GitHub")
|
.append(TextComponent.builder("Velocity GitHub")
|
||||||
.color(TextColor.GREEN)
|
.color(TextColor.GREEN)
|
||||||
.clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/astei/velocity"))
|
.clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/astei/velocity"))
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package com.velocitypowered.proxy.console;
|
package com.velocitypowered.proxy.console;
|
||||||
|
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import net.kyori.text.TextComponent;
|
||||||
|
import net.kyori.text.format.TextColor;
|
||||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||||
import org.jline.reader.LineReader;
|
import org.jline.reader.*;
|
||||||
import org.jline.reader.LineReaderBuilder;
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class VelocityConsole extends SimpleTerminalConsole {
|
public final class VelocityConsole extends SimpleTerminalConsole {
|
||||||
|
|
||||||
@ -17,7 +20,13 @@ public final class VelocityConsole extends SimpleTerminalConsole {
|
|||||||
protected LineReader buildReader(LineReaderBuilder builder) {
|
protected LineReader buildReader(LineReaderBuilder builder) {
|
||||||
return super.buildReader(builder
|
return super.buildReader(builder
|
||||||
.appName("Velocity")
|
.appName("Velocity")
|
||||||
// TODO: Command completion
|
.completer((reader, parsedLine, list) -> {
|
||||||
|
List<String> offers = server.getCommandManager().offerSuggestions(server.getConsoleCommandInvoker(), parsedLine.line());
|
||||||
|
for (String offer : offers) {
|
||||||
|
if (offer.isEmpty()) continue;
|
||||||
|
list.add(new Candidate(offer));
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +37,9 @@ public final class VelocityConsole extends SimpleTerminalConsole {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void runCommand(String command) {
|
protected void runCommand(String command) {
|
||||||
this.server.getCommandManager().execute(this.server.getConsoleCommandInvoker(), command);
|
if (!this.server.getCommandManager().execute(this.server.getConsoleCommandInvoker(), command)) {
|
||||||
|
server.getConsoleCommandInvoker().sendMessage(TextComponent.of("Command not found.", TextColor.RED));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren