231 lines
8.9 KiB
Java
231 lines
8.9 KiB
Java
package ru.gravit.launchserver.command.handler;
|
|
|
|
import java.io.IOException;
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.LinkedList;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import ru.gravit.launchserver.LaunchServer;
|
|
import ru.gravit.launchserver.command.Command;
|
|
import ru.gravit.launchserver.command.CommandException;
|
|
import ru.gravit.launchserver.command.auth.AuthCommand;
|
|
import ru.gravit.launchserver.command.auth.BanCommand;
|
|
import ru.gravit.launchserver.command.auth.UUIDToUsernameCommand;
|
|
import ru.gravit.launchserver.command.auth.UnbanCommand;
|
|
import ru.gravit.launchserver.command.auth.UsernameToUUIDCommand;
|
|
import ru.gravit.launchserver.command.basic.BuildCommand;
|
|
import ru.gravit.launchserver.command.basic.ClearCommand;
|
|
import ru.gravit.launchserver.command.basic.DebugCommand;
|
|
import ru.gravit.launchserver.command.basic.GCCommand;
|
|
import ru.gravit.launchserver.command.basic.HelpCommand;
|
|
import ru.gravit.launchserver.command.basic.LogConnectionsCommand;
|
|
import ru.gravit.launchserver.command.basic.ProguardCleanCommand;
|
|
import ru.gravit.launchserver.command.basic.RebindCommand;
|
|
import ru.gravit.launchserver.command.basic.RegenProguardDictCommand;
|
|
import ru.gravit.launchserver.command.basic.RemoveMappingsProguardCommand;
|
|
import ru.gravit.launchserver.command.basic.StopCommand;
|
|
import ru.gravit.launchserver.command.basic.TestCommand;
|
|
import ru.gravit.launchserver.command.basic.VersionCommand;
|
|
import ru.gravit.launchserver.command.dump.DumpEntryCacheCommand;
|
|
import ru.gravit.launchserver.command.dump.DumpSessionsCommand;
|
|
import ru.gravit.launchserver.command.hash.DownloadAssetCommand;
|
|
import ru.gravit.launchserver.command.hash.DownloadClientCommand;
|
|
import ru.gravit.launchserver.command.hash.IndexAssetCommand;
|
|
import ru.gravit.launchserver.command.hash.SyncBinariesCommand;
|
|
import ru.gravit.launchserver.command.hash.SyncProfilesCommand;
|
|
import ru.gravit.launchserver.command.hash.SyncUpdatesCommand;
|
|
import ru.gravit.launchserver.command.hash.UnindexAssetCommand;
|
|
import ru.gravit.launchserver.command.modules.LoadModuleCommand;
|
|
import ru.gravit.launchserver.command.modules.ModulesCommand;
|
|
import ru.gravit.launchserver.command.service.ReloadAllCommand;
|
|
import ru.gravit.launchserver.command.service.ReloadCommand;
|
|
import ru.gravit.launchserver.command.service.ReloadInfoCommand;
|
|
import ru.gravit.utils.helper.LogHelper;
|
|
import ru.gravit.utils.helper.VerifyHelper;
|
|
|
|
public abstract class CommandHandler implements Runnable {
|
|
private static String[] parse(CharSequence line) throws CommandException {
|
|
boolean quoted = false;
|
|
boolean wasQuoted = false;
|
|
|
|
// Read line char by char
|
|
Collection<String> result = new LinkedList<>();
|
|
StringBuilder builder = new StringBuilder(100);
|
|
for (int i = 0; i <= line.length(); i++) {
|
|
boolean end = i >= line.length();
|
|
char ch = end ? '\0' : line.charAt(i);
|
|
|
|
// Maybe we should read next argument?
|
|
if (end || !quoted && Character.isWhitespace(ch)) {
|
|
if (end && quoted)
|
|
throw new CommandException("Quotes wasn't closed");
|
|
|
|
// Empty args are ignored (except if was quoted)
|
|
if (wasQuoted || builder.length() > 0)
|
|
result.add(builder.toString());
|
|
|
|
// Reset string builder
|
|
wasQuoted = false;
|
|
builder.setLength(0);
|
|
continue;
|
|
}
|
|
|
|
// Append next char
|
|
switch (ch) {
|
|
case '"': // "abc"de, "abc""de" also allowed
|
|
quoted = !quoted;
|
|
wasQuoted = true;
|
|
break;
|
|
case '\\': // All escapes, including spaces etc
|
|
if (i + 1 >= line.length())
|
|
throw new CommandException("Escape character is not specified");
|
|
char next = line.charAt(i + 1);
|
|
builder.append(next);
|
|
i++;
|
|
break;
|
|
default: // Default char, simply append
|
|
builder.append(ch);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return result as array
|
|
return result.toArray(new String[0]);
|
|
}
|
|
|
|
private final Map<String, Command> commands = new ConcurrentHashMap<>(32);
|
|
|
|
protected CommandHandler(LaunchServer server) {
|
|
// Register basic commands
|
|
registerCommand("help", new HelpCommand(server));
|
|
registerCommand("version", new VersionCommand(server));
|
|
registerCommand("build", new BuildCommand(server));
|
|
registerCommand("stop", new StopCommand(server));
|
|
registerCommand("rebind", new RebindCommand(server));
|
|
registerCommand("debug", new DebugCommand(server));
|
|
registerCommand("clear", new ClearCommand(server));
|
|
registerCommand("gc", new GCCommand(server));
|
|
registerCommand("proguardClean", new ProguardCleanCommand(server));
|
|
registerCommand("proguardDictRegen", new RegenProguardDictCommand(server));
|
|
registerCommand("proguardMappingsRemove", new RemoveMappingsProguardCommand(server));
|
|
registerCommand("logConnections", new LogConnectionsCommand(server));
|
|
registerCommand("loadModule", new LoadModuleCommand(server));
|
|
registerCommand("modules", new ModulesCommand(server));
|
|
registerCommand("test", new TestCommand(server));
|
|
|
|
// Register sync commands
|
|
registerCommand("indexAsset", new IndexAssetCommand(server));
|
|
registerCommand("unindexAsset", new UnindexAssetCommand(server));
|
|
registerCommand("downloadAsset", new DownloadAssetCommand(server));
|
|
registerCommand("downloadClient", new DownloadClientCommand(server));
|
|
registerCommand("syncBinaries", new SyncBinariesCommand(server));
|
|
registerCommand("syncUpdates", new SyncUpdatesCommand(server));
|
|
registerCommand("syncProfiles", new SyncProfilesCommand(server));
|
|
|
|
// Register auth commands
|
|
registerCommand("auth", new AuthCommand(server));
|
|
registerCommand("usernameToUUID", new UsernameToUUIDCommand(server));
|
|
registerCommand("uuidToUsername", new UUIDToUsernameCommand(server));
|
|
registerCommand("ban", new BanCommand(server));
|
|
registerCommand("unban", new UnbanCommand(server));
|
|
|
|
//Register dump commands
|
|
registerCommand("dumpSessions", new DumpSessionsCommand(server));
|
|
registerCommand("dumpEntryCache", new DumpEntryCacheCommand(server));
|
|
|
|
//Register service commands
|
|
registerCommand("reload",new ReloadCommand(server));
|
|
registerCommand("reloadAll",new ReloadAllCommand(server));
|
|
registerCommand("reloadInfo",new ReloadInfoCommand(server));
|
|
}
|
|
|
|
|
|
public abstract void bell() throws IOException;
|
|
|
|
|
|
public abstract void clear() throws IOException;
|
|
|
|
|
|
public final Map<String, Command> commandsMap() {
|
|
return Collections.unmodifiableMap(commands);
|
|
}
|
|
|
|
|
|
public final void eval(String line, boolean bell) {
|
|
LogHelper.info("Command '%s'", line);
|
|
|
|
// Parse line to tokens
|
|
String[] args;
|
|
try {
|
|
args = parse(line);
|
|
} catch (Exception e) {
|
|
LogHelper.error(e);
|
|
return;
|
|
}
|
|
|
|
// Evaluate command
|
|
eval(args, bell);
|
|
}
|
|
|
|
|
|
public final void eval(String[] args, boolean bell) {
|
|
if (args.length == 0)
|
|
return;
|
|
|
|
// Measure start time and invoke command
|
|
Instant startTime = Instant.now();
|
|
try {
|
|
lookup(args[0]).invoke(Arrays.copyOfRange(args, 1, args.length));
|
|
} catch (Exception e) {
|
|
LogHelper.error(e);
|
|
}
|
|
|
|
// Bell if invocation took > 1s
|
|
Instant endTime = Instant.now();
|
|
if (bell && Duration.between(startTime, endTime).getSeconds() >= 5)
|
|
try {
|
|
bell();
|
|
} catch (IOException e) {
|
|
LogHelper.error(e);
|
|
}
|
|
}
|
|
|
|
|
|
public final Command lookup(String name) throws CommandException {
|
|
Command command = commands.get(name);
|
|
if (command == null)
|
|
throw new CommandException(String.format("Unknown command: '%s'", name));
|
|
return command;
|
|
}
|
|
|
|
|
|
public abstract String readLine() throws IOException;
|
|
|
|
private void readLoop() throws IOException {
|
|
for (String line = readLine(); line != null; line = readLine())
|
|
eval(line, true);
|
|
}
|
|
|
|
|
|
public final void registerCommand(String name, Command command) {
|
|
VerifyHelper.verifyIDName(name);
|
|
VerifyHelper.putIfAbsent(commands, name, Objects.requireNonNull(command, "command"),
|
|
String.format("Command has been already registered: '%s'", name));
|
|
}
|
|
|
|
@Override
|
|
public final void run() {
|
|
try {
|
|
readLoop();
|
|
} catch (IOException e) {
|
|
LogHelper.error(e);
|
|
}
|
|
}
|
|
}
|