13
0
geforkt von Mirrors/Velocity

Add console tab complete, shutdown command, gracefully kick players.

Dieser Commit ist enthalten in:
Andrew Steinborn 2018-08-07 11:02:35 -04:00
Ursprung beb947cc3e
Commit bb601dca4b
6 geänderte Dateien mit 101 neuen und 10 gelöschten Zeilen

Datei anzeigen

@ -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() {

Datei anzeigen

@ -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);
}
}
} }

Datei anzeigen

@ -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();
}
}
} }

Datei anzeigen

@ -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();
}
}

Datei anzeigen

@ -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"))

Datei anzeigen

@ -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