re-implement the commands, right usage of logger, general improvements to the listener (#39)

Co-authored-by: mohammed jasem alaajel <xrambad@gmail.com>
This commit is contained in:
Adrian 2022-07-15 01:52:53 -05:00 committed by GitHub
parent 5f07ba6b66
commit 21f6cdeaeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 404 additions and 364 deletions

View File

@ -12,8 +12,8 @@
<artifactId>RedisBungee-Velocity</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<repositories>
<repository>
@ -48,8 +48,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
@ -130,7 +130,7 @@
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.1.0</version>
<version>3.1.2-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>

View File

@ -4,13 +4,10 @@ package com.imaginarycode.minecraft.redisbungee;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.permission.Tristate;
import net.kyori.adventure.permission.PermissionChecker;
import net.kyori.adventure.util.TriState;
import java.util.Collection;
import java.util.Collections;
public class RedisBungeeCommandSource implements CommandSource {
private static final RedisBungeeCommandSource singleton;
private final PermissionChecker permissionChecker = PermissionChecker.always(net.kyori.adventure.util.TriState.TRUE);
static {
singleton = new RedisBungeeCommandSource();
@ -23,16 +20,16 @@ public class RedisBungeeCommandSource implements CommandSource {
@Override
public boolean hasPermission(String permission) {
return true;
return this.permissionChecker.test(permission);
}
@Override
public Tristate getPermissionValue(String s) {
return null;
return Tristate.TRUE;
}
@Override
public PermissionChecker getPermissionChecker() {
return PermissionChecker.always(TriState.TRUE);
return this.permissionChecker;
}
}

View File

@ -20,21 +20,23 @@ import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent.ForwardResult;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.server.ServerPing;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.net.InetAddress;
import java.util.*;
import java.util.stream.Collectors;
public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener<LoginEvent, PostLoginEvent, DisconnectEvent, ServerConnectedEvent, ProxyPingEvent, PluginMessageEvent, PubSubMessageEvent> {
// Some messages are using legacy characters
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
public RedisBungeeVelocityListener(RedisBungeePlugin<?> plugin, List<InetAddress> exemptAddresses) {
super(plugin, exemptAddresses);
@ -56,14 +58,14 @@ public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener<Log
Player player = (Player) plugin.getPlayer(event.getPlayer().getUsername());
if (player != null) {
event.setResult(ResultedEvent.ComponentResult.denied(Component.text(ONLINE_MODE_RECONNECT)));
event.setResult(ResultedEvent.ComponentResult.denied(serializer.deserialize(ONLINE_MODE_RECONNECT)));
return null;
}
}
for (String s : plugin.getServerIds()) {
if (jedis.sismember("proxy:" + s + ":usersOnline", event.getPlayer().getUniqueId().toString())) {
event.setResult(ResultedEvent.ComponentResult.denied(Component.text(ALREADY_LOGGED_IN)));
event.setResult(ResultedEvent.ComponentResult.denied(serializer.deserialize(ALREADY_LOGGED_IN)));
return null;
}
}
@ -136,114 +138,115 @@ public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener<Log
if (exemptAddresses.contains(event.getConnection().getRemoteAddress().getAddress())) {
return;
}
ServerPing oldPing = event.getPing();
int max = oldPing.getPlayers().map(ServerPing.Players::getMax).orElse(0);
List<ServerPing.SamplePlayer> list = oldPing.getPlayers().map(ServerPing.Players::getSample).orElse(Collections.emptyList());
event.setPing(new ServerPing(oldPing.getVersion(), new ServerPing.Players(plugin.getCount(), max, list), oldPing.getDescriptionComponent(), oldPing.getFavicon().orElse(null)));
ServerPing.Builder ping = event.getPing().asBuilder();
ping.onlinePlayers(plugin.getCount());
event.setPing(ping.build());
}
@Override
@SuppressWarnings("UnstableApiUsage")
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
if ((event.getIdentifier().getId().equals("legacy:redisbungee") || event.getIdentifier().getId().equals("RedisBungee")) && event.getSource() instanceof ServerConnection) {
final String currentChannel = event.getIdentifier().getId();
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
plugin.executeAsync(() -> {
ByteArrayDataInput in = ByteStreams.newDataInput(data);
String subchannel = in.readUTF();
ByteArrayDataOutput out = ByteStreams.newDataOutput();
String type;
switch (subchannel) {
case "PlayerList":
out.writeUTF("PlayerList");
Set<UUID> original = Collections.emptySet();
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
original = plugin.getPlayers();
} else {
try {
original = plugin.getApi().getPlayersOnServer(type);
} catch (IllegalArgumentException ignored) {
}
}
Set<String> players = new HashSet<>();
for (UUID uuid : original)
players.add(plugin.getUuidTranslator().getNameFromUuid(uuid, false));
out.writeUTF(Joiner.on(',').join(players));
break;
case "PlayerCount":
out.writeUTF("PlayerCount");
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
out.writeInt(plugin.getCount());
} else {
out.writeUTF(type);
try {
out.writeInt(plugin.getApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) {
out.writeInt(0);
}
}
break;
case "LastOnline":
String user = in.readUTF();
out.writeUTF("LastOnline");
out.writeUTF(user);
out.writeLong(plugin.getApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
break;
case "ServerPlayers":
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT":
includesUsers = false;
break;
case "PLAYERS":
includesUsers = true;
break;
default:
// TODO: Should I raise an error?
return;
}
out.writeUTF(type1);
if (includesUsers) {
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : multimap.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
serializeMultimap(human, true, out);
} else {
serializeMultiset(multimap.keys(), out);
}
break;
case "Proxy":
out.writeUTF("Proxy");
out.writeUTF(plugin.getConfiguration().getServerId());
break;
case "PlayerProxy":
String username = in.readUTF();
out.writeUTF("PlayerProxy");
out.writeUTF(username);
out.writeUTF(plugin.getApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
break;
default:
return;
}
((ServerConnection) event.getSource()).sendPluginMessage(new LegacyChannelIdentifier(currentChannel), out.toByteArray());
});
if(!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
return;
}
event.setResult(ForwardResult.handled());
plugin.executeAsync(() -> {
ByteArrayDataInput in = event.dataAsDataStream();
String subchannel = in.readUTF();
ByteArrayDataOutput out = ByteStreams.newDataOutput();
String type;
switch (subchannel) {
case "PlayerList":
out.writeUTF("PlayerList");
Set<UUID> original = Collections.emptySet();
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
original = plugin.getPlayers();
} else {
try {
original = plugin.getApi().getPlayersOnServer(type);
} catch (IllegalArgumentException ignored) {
}
}
Set<String> players = original.stream()
.map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
.collect(Collectors.toSet());
out.writeUTF(Joiner.on(',').join(players));
break;
case "PlayerCount":
out.writeUTF("PlayerCount");
type = in.readUTF();
if (type.equals("ALL")) {
out.writeUTF("ALL");
out.writeInt(plugin.getCount());
} else {
out.writeUTF(type);
try {
out.writeInt(plugin.getApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) {
out.writeInt(0);
}
}
break;
case "LastOnline":
String user = in.readUTF();
out.writeUTF("LastOnline");
out.writeUTF(user);
out.writeLong(plugin.getApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
break;
case "ServerPlayers":
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT":
includesUsers = false;
break;
case "PLAYERS":
includesUsers = true;
break;
default:
// TODO: Should I raise an error?
return;
}
out.writeUTF(type1);
if (includesUsers) {
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : multimap.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
serializeMultimap(human, true, out);
} else {
serializeMultiset(multimap.keys(), out);
}
break;
case "Proxy":
out.writeUTF("Proxy");
out.writeUTF(plugin.getConfiguration().getServerId());
break;
case "PlayerProxy":
String username = in.readUTF();
out.writeUTF("PlayerProxy");
out.writeUTF(username);
out.writeUTF(plugin.getApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
break;
default:
return;
}
((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
});
}
@ -255,7 +258,7 @@ public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener<Log
if (message.startsWith("/"))
message = message.substring(1);
plugin.logInfo("Invoking command via PubSub: /" + message);
((RedisBungeeVelocityPlugin) plugin).getProxy().getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), message);//.dispatchCommand(RedisBungeeCommandSource.getSingleton(), message);
((RedisBungeeVelocityPlugin)plugin).getProxy().getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), message);
}
}

View File

@ -6,9 +6,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
@ -29,6 +29,7 @@ import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.scheduler.ScheduledTask;
@ -48,6 +49,7 @@ import java.io.*;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@ -56,7 +58,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
private final ProxyServer server;
private final Logger logger;
private final File dataFolder;
private final Path dataFolder;
private final RedisBungeeAPI api;
private final PubSubListener psl;
private JedisSummoner jedisSummoner;
@ -73,6 +75,10 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
private LuaManager.Script getPlayerCountScript;
private static final Object SERVER_TO_PLAYERS_KEY = new Object();
public static final List<ChannelIdentifier> IDENTIFIERS = List.of(
MinecraftChannelIdentifier.create("legacy", "redisbungee"),
new LegacyChannelIdentifier("RedisBungee")
);
private final Cache<Object, Multimap<String, UUID>> serverToPlayersCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
@ -82,7 +88,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
public RedisBungeeVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.server = server;
this.logger = logger;
this.dataFolder = dataDirectory.toFile();
this.dataFolder = dataDirectory;
try {
loadConfig();
} catch (IOException e) {
@ -107,9 +113,9 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
for (String s : info.split("\r\n")) {
if (s.startsWith("redis_version:")) {
String version = s.split(":")[1];
getLogger().info(version + " <- redis version");
getLogger().info("{} <- redis version", version);
if (!RedisUtil.isRedisVersionRight(version)) {
getLogger().error("Your version of Redis (" + version + ") is not at least version 6.0 RedisBungee requires a newer version of Redis.");
getLogger().error("Your version of Redis ({}) is not at least version 6.0 RedisBungee requires a newer version of Redis.", version);
throw new RuntimeException("Unsupported Redis version detected");
} else {
LuaManager manager = new LuaManager(this);
@ -272,7 +278,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
if (lagged ? time >= stamp + 30 : time <= stamp + 30)
servers.add(entry.getKey());
else if (nag && nagTime <= 0) {
getLogger().warn(entry.getKey() + " is " + (time - stamp) + " seconds behind! (Time not synchronized or server down?) and was removed from heartbeat.");
getLogger().warn("{} is {} seconds behind! (Time not synchronized or server down?) and was removed from heartbeat.", entry.getKey(), (time - stamp));
jedis.hdel("heartbeats", entry.getKey());
}
} catch (NumberFormatException ignored) {
@ -308,12 +314,12 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
@Override
public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
this.getProxy().getScheduler().buildTask(this, runnable).delay(time, timeUnit);
this.getProxy().getScheduler().buildTask(this, runnable).delay(time, timeUnit).schedule();
}
@Override
public void callEvent(Object event) {
this.getProxy().getEventManager().fire(event);
this.getProxy().getEventManager().fireAndForget(event);
}
@Override
@ -425,7 +431,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
Set<String> laggedPlayers = tmpRsc.smembers("proxy:" + s + ":usersOnline");
tmpRsc.del("proxy:" + s + ":usersOnline");
if (!laggedPlayers.isEmpty()) {
getLogger().info("Cleaning up lagged proxy " + s + " (" + laggedPlayers.size() + " players)...");
getLogger().info("Cleaning up lagged proxy {} ({} players)...", s, laggedPlayers.size());
for (String laggedPlayer : laggedPlayers) {
RedisUtil.cleanUpPlayer(laggedPlayer, tmpRsc);
}
@ -449,10 +455,10 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
}
if (!found) {
RedisUtil.cleanUpPlayer(member, tmpRsc);
getLogger().warn("Player found in set that was not found locally and globally: " + member);
getLogger().warn("Player found in set that was not found locally and globally: {}", member);
} else {
tmpRsc.srem("proxy:" + configuration.getServerId() + ":usersOnline", member);
getLogger().warn("Player found in set that was not found locally, but is on another proxy: " + member);
getLogger().warn("Player found in set that was not found locally, but is on another proxy: {}", member);
}
}
@ -460,7 +466,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
for (String player : absentInRedis) {
// Player not online according to Redis but not BungeeCord.
getLogger().warn("Player " + player + " is on the proxy but not in Redis.");
getLogger().warn("Player {} is on the proxy but not in Redis.", player);
Player playerProxied = getProxy().getPlayer(UUID.fromString(player)).orElse(null);
if (playerProxied == null)
@ -475,24 +481,23 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
}
}).repeat(1, TimeUnit.MINUTES).schedule();
// register plugin messages
IDENTIFIERS.forEach(getProxy().getChannelRegistrar()::register);
getProxy().getChannelRegistrar().register(new LegacyChannelIdentifier("RedisBungee"));
getProxy().getChannelRegistrar().register(new LegacyChannelIdentifier("legacy:redisbungee"));
getProxy().getChannelRegistrar().register(MinecraftChannelIdentifier.create("legacy", "redisbungee"));
// register commands
// those still bungeecord commands will migrate them later.
// if (configuration.doOverrideBungeeCommands()) {
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.FindCommand(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.LastSeenCommand(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand(this));
//
// }
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerIds(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlayerProxyCommand(this));
// getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlistCommand(this));
// Override Velocity commands
if (configuration.doOverrideBungeeCommands()) {
getProxy().getCommandManager().register("glist", new RedisBungeeCommands.GlistCommand(this), "redisbungee", "rglist");
}
getProxy().getCommandManager().register("sendtoall", new RedisBungeeCommands.SendToAll(this), "rsendtoall");
getProxy().getCommandManager().register("serverid", new RedisBungeeCommands.ServerId(this), "rserverid");
getProxy().getCommandManager().register("serverids", new RedisBungeeCommands.ServerIds(this));
getProxy().getCommandManager().register("pproxy", new RedisBungeeCommands.PlayerProxyCommand(this));
getProxy().getCommandManager().register("plist", new RedisBungeeCommands.PlistCommand(this), "rplist");
getProxy().getCommandManager().register("lastseen", new RedisBungeeCommands.LastSeenCommand(this), "rlastseen");
getProxy().getCommandManager().register("ip", new RedisBungeeCommands.IpCommand(this), "playerip", "rip", "rplayerip");
getProxy().getCommandManager().register("find", new RedisBungeeCommands.FindCommand(this), "rfind");
}
@Override
@ -526,18 +531,26 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
@Override
public void loadConfig() throws IOException {
if (!getDataFolder().exists() && getDataFolder().mkdir()) {
getLogger().info("data folder was created");
if (Files.notExists(getDataFolder())) {
try {
Files.createDirectory(getDataFolder());
getLogger().info("data folder was created");
} catch (IOException e) {
getLogger().error("Cannot create data folder", e);
}
}
File file = new File(getDataFolder(), "config.yml");
if (!file.exists() && file.createNewFile()) {
getLogger().info("config file was created");
try (InputStream in = getResourceAsStream("example_config.yml");
OutputStream out = Files.newOutputStream(file.toPath())) {
ByteStreams.copy(in, out);
Path file = getDataFolder().resolve("config.yml");
if (Files.notExists(file)) {
try (InputStream in = getResourceAsStream("example_config.yml")) {
Files.createFile(file);
Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
getLogger().info("config file was created");
} catch (IOException e) {
getLogger().error("Cannot create configuration", e);
}
}
final YAMLConfigurationLoader yamlConfiguration = YAMLConfigurationLoader.builder().setFile(file).build();
final YAMLConfigurationLoader yamlConfiguration = YAMLConfigurationLoader.builder().setPath(file).build();
ConfigurationNode node = yamlConfiguration.load();
final String redisServer = node.getNode("redis-server").getString();
final int redisPort = node.getNode("redis-port").getInt();
@ -560,12 +573,12 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
* I think due snake yaml limitations so as todo: write our own yaml parser?
*/
String genId = UUID.randomUUID().toString();
getLogger().info("Generated server id " + genId + " and saving it to config.");
getLogger().info("Generated server id {} and saving it to config.", genId);
node.getNode("server-id").setValue(genId);
yamlConfiguration.save(node);
getLogger().info("Server id was generated: " + serverId);
getLogger().info("Server id was generated: {}", serverId);
} else {
getLogger().info("Loaded server id " + serverId + '.');
getLogger().info("Loaded server id {}.", serverId);
}
try {
this.configuration = new RedisBungeeConfiguration(serverId, node.getNode("exempt-ip-addresses").getList(TypeToken.of(String.class)), node.getNode("register-bungee-commands").getBoolean());
@ -587,9 +600,15 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
try (Jedis rsc = requestJedis()) {
rsc.ping();
// If that worked, now we can check for an existing, alive Bungee:
File crashFile = new File(getDataFolder(), "restarted_from_crash.txt");
if (crashFile.exists() && crashFile.delete()) {
getLogger().info("crash file was deleted");
Path crashFile = getDataFolder().resolve("restarted_from_crash.txt");
if (Files.exists(crashFile)) {
try {
Files.delete(crashFile);
getLogger().info("crash file was deleted");
} catch (IOException e) {
getLogger().error("Cannot delete crash file", e);
}
} else if (rsc.hexists("heartbeats", serverId)) {
try {
long value = Long.parseLong(rsc.hget("heartbeats", serverId));
@ -653,7 +672,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
return logger;
}
public File getDataFolder() {
public Path getDataFolder() {
return this.dataFolder;
}

View File

@ -1,6 +1,25 @@
package com.imaginarycode.minecraft.redisbungee.commands;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeVelocityPlugin;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
/**
@ -12,316 +31,318 @@ import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
* @since 0.2.3
*/
public class RedisBungeeCommands {
/*
private static final BaseComponent[] NO_PLAYER_SPECIFIED =
new ComponentBuilder("You must specify a player name.").color(ChatColor.RED).create();
private static final BaseComponent[] PLAYER_NOT_FOUND =
new ComponentBuilder("No such player found.").color(ChatColor.RED).create();
private static final BaseComponent[] NO_COMMAND_SPECIFIED =
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
private static final Component NO_PLAYER_SPECIFIED =
Component.text("You must specify a player name.", NamedTextColor.RED);
private static final Component PLAYER_NOT_FOUND =
Component.text("No such player found.", NamedTextColor.RED);
private static final Component NO_COMMAND_SPECIFIED =
Component.text("You must specify a command to be run.", NamedTextColor.RED);
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
public static class GlistCommand extends Command {
public static class GlistCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public GlistCommand(RedisBungeeVelocityPlugin plugin) {
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
int count = plugin.getApi().getPlayerCount();
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(count) + " currently online.").create();
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.GREEN);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
public void execute(final Invocation invocation) {
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
int count = plugin.getApi().getPlayerCount();
Component playersOnline = Component.text(playerPlural(count) + " currently online.", NamedTextColor.YELLOW);
CommandSource sender = invocation.source();
if (invocation.arguments().length > 0 && invocation.arguments()[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false)));
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN);
Component serverCount = Component.text("(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW);
Component serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sender.sendMessage(Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(Component.text("To see all players online, use /glist showall.", NamedTextColor.YELLOW));
}
});
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("velocity.command.server");
}
}
public static class FindCommand extends Command {
public static class FindCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public FindCommand(RedisBungeeVelocityPlugin plugin) {
super("find", "bungeecord.command.find", "rfind");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
ServerInfo si = plugin.getProxy().getServerInfo(plugin.getApi().getServerFor(uuid));
if (si != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " is on " + si.getName() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
public void execute(final Invocation invocation) {
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
String[] args = invocation.arguments();
CommandSource sender = invocation.source();
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
ServerInfo si = plugin.getProxy().getServer(plugin.getApi().getServerFor(uuid)).map(RegisteredServer::getServerInfo).orElse(null);
if (si != null) {
Component message = Component.text(args[0] + " is on " + si.getName() + ".", NamedTextColor.BLUE);
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
});
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.find");
}
}
public static class LastSeenCommand extends Command {
public static class LastSeenCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public LastSeenCommand(RedisBungeeVelocityPlugin plugin) {
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
long secs = plugin.getApi().getLastOnline(uuid);
TextComponent message = new TextComponent();
if (secs == 0) {
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is currently online.");
} else if (secs != -1) {
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.setColor(ChatColor.RED);
message.setText(args[0] + " has never been online.");
}
sender.sendMessage(message);
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
public void execute(final Invocation invocation) {
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
String[] args = invocation.arguments();
CommandSource sender = invocation.source();
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
long secs = plugin.getApi().getLastOnline(uuid);
TextComponent.Builder message = Component.text();
if (secs == 0) {
message.color(NamedTextColor.GREEN);
message.content(args[0] + " is currently online.");
} else if (secs != -1) {
message.color(NamedTextColor.BLUE);
message.content(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.color(NamedTextColor.RED);
message.content(args[0] + " has never been online.");
}
sender.sendMessage(message.build());
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
});
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.lastseen");
}
}
public static class IpCommand extends Command {
public static class IpCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public IpCommand(RedisBungeeVelocityPlugin plugin) {
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
InetAddress ia = plugin.getApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected from " + ia.toString() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
public void execute(final Invocation invocation) {
CommandSource sender = invocation.source();
String[] args = invocation.arguments();
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
InetAddress ia = plugin.getApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = Component.text(args[0] + " is connected from " + ia.toString() + ".", NamedTextColor.GREEN);
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
});
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.ip");
}
}
public static class PlayerProxyCommand extends Command {
public static class PlayerProxyCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public PlayerProxyCommand(RedisBungeeVelocityPlugin plugin) {
super("pproxy", "redisbungee.command.pproxy");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
String proxy = plugin.getApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected to " + proxy + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
public void execute(final Invocation invocation) {
CommandSource sender = invocation.source();
String[] args = invocation.arguments();
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
String proxy = plugin.getApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = Component.text(args[0] + " is connected to " + proxy + ".", NamedTextColor.GREEN);
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
});
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.pproxy");
}
}
public static class SendToAll extends Command {
public static class SendToAll implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public SendToAll(RedisBungeeVelocityPlugin plugin) {
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
//super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
public void execute(final Invocation invocation) {
String[] args = invocation.arguments();
CommandSource sender = invocation.source();
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
plugin.getApi().sendProxyCommand(command);
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText("Sent the command /" + command + " to all proxies.");
TextComponent message = Component.text("Sent the command /" + command + " to all proxies.", NamedTextColor.GREEN);
sender.sendMessage(message);
} else {
sender.sendMessage(NO_COMMAND_SPECIFIED);
}
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.sendtoall");
}
}
public static class ServerId extends Command {
public static class ServerId implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public ServerId(RedisBungeeVelocityPlugin plugin) {
super("serverid", "redisbungee.command.serverid", "rserverid");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
TextComponent textComponent = new TextComponent();
textComponent.setText("You are on " + plugin.getApi().getServerId() + ".");
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
public void execute(Invocation invocation) {
invocation.source().sendMessage(Component.text("You are on " + plugin.getApi().getServerId() + ".", NamedTextColor.YELLOW));
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.serverid");
}
}
public static class ServerIds extends Command {
public static class ServerIds implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public ServerIds(RedisBungeeVelocityPlugin plugin) {
super("serverids", "redisbungee.command.serverids");
this.plugin =plugin;
}
@Override
public void execute(CommandSender sender, String[] strings) {
TextComponent textComponent = new TextComponent();
textComponent.setText("All server IDs: " + Joiner.on(", ").join(plugin.getApi().getAllServers()));
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
public void execute(Invocation invocation) {
invocation.source().sendMessage(
Component.text("All server IDs: " + Joiner.on(", ").join(plugin.getApi().getAllServers()), NamedTextColor.YELLOW));
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.serverids");
}
}
public static class PlistCommand extends Command {
public static class PlistCommand implements SimpleCommand {
private final RedisBungeeVelocityPlugin plugin;
public PlistCommand(RedisBungeeVelocityPlugin plugin) {
super("plist", "redisbungee.command.plist", "rplist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getServerId();
if (!plugin.getServerIds().contains(proxy)) {
sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
return;
}
Set<UUID> players = plugin.getApi().getPlayersOnProxy(proxy);
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(players.size()) + " currently on proxy " + proxy + ".").create();
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
if (players.contains(entry.getValue())) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
}
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.RED);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + human.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /plist " + proxy + " showall.").color(ChatColor.YELLOW).create());
}
public void execute(Invocation invocation) {
CommandSource sender= invocation.source();
String[] args = invocation.arguments();
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getServerId();
if (!plugin.getServerIds().contains(proxy)) {
sender.sendMessage(Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED));
return;
}
});
Set<UUID> players = plugin.getApi().getPlayersOnProxy(proxy);
Component playersOnline = Component.text(playerPlural(players.size()) + " currently on proxy " + proxy + ".", NamedTextColor.YELLOW);
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
if (players.contains(value)) {
human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false));
}
});
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED);
TextComponent serverCount = Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW);
TextComponent serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sender.sendMessage(Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(Component.text("To see all players online, use /plist " + proxy + " showall.", NamedTextColor.YELLOW));
}
}).schedule();
}
@Override
public boolean hasPermission(Invocation invocation) {
return invocation.source().hasPermission("redisbungee.command.plist");
}
}
*/
}