From 21f6cdeaeb4cc0107570bc7b4349e7611e5b0a2e Mon Sep 17 00:00:00 2001 From: Adrian <68704415+4drian3d@users.noreply.github.com> Date: Fri, 15 Jul 2022 01:52:53 -0500 Subject: [PATCH] re-implement the commands, right usage of logger, general improvements to the listener (#39) Co-authored-by: mohammed jasem alaajel --- RedisBungee-Velocity/pom.xml | 10 +- .../redisbungee/RedisBungeeCommandSource.java | 11 +- .../RedisBungeeVelocityListener.java | 219 ++++----- .../RedisBungeeVelocityPlugin.java | 107 +++-- .../commands/RedisBungeeCommands.java | 421 +++++++++--------- 5 files changed, 404 insertions(+), 364 deletions(-) diff --git a/RedisBungee-Velocity/pom.xml b/RedisBungee-Velocity/pom.xml index 73ad06b..b96873f 100644 --- a/RedisBungee-Velocity/pom.xml +++ b/RedisBungee-Velocity/pom.xml @@ -12,8 +12,8 @@ RedisBungee-Velocity - 8 - 8 + 11 + 11 @@ -48,8 +48,8 @@ maven-compiler-plugin 3.8.1 - 1.8 - 1.8 + 11 + 11 @@ -130,7 +130,7 @@ com.velocitypowered velocity-api - 3.1.0 + 3.1.2-SNAPSHOT jar provided diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSource.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSource.java index f843028..c0e8d29 100644 --- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSource.java +++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSource.java @@ -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; } } diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java index 1d8fb44..4119e73 100644 --- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java +++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java @@ -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 { - + // Some messages are using legacy characters + private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection(); public RedisBungeeVelocityListener(RedisBungeePlugin plugin, List exemptAddresses) { super(plugin, exemptAddresses); @@ -56,14 +58,14 @@ public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener 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 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 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 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 human = HashMultimap.create(); - for (Map.Entry 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 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 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 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 human = HashMultimap.create(); + for (Map.Entry 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 { 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 { private LuaManager.Script getPlayerCountScript; private static final Object SERVER_TO_PLAYERS_KEY = new Object(); + public static final List IDENTIFIERS = List.of( + MinecraftChannelIdentifier.create("legacy", "redisbungee"), + new LegacyChannelIdentifier("RedisBungee") + ); private final Cache> serverToPlayersCache = CacheBuilder.newBuilder() .expireAfterWrite(5, TimeUnit.SECONDS) .build(); @@ -82,7 +88,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin { 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 { 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 { 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 { @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 { Set 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 { } 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 { 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 { } }).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 { @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 { * 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 { 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 { return logger; } - public File getDataFolder() { + public Path getDataFolder() { return this.dataFolder; } diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java index 899f7b5..56d647c 100644 --- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java +++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java @@ -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 serverToPlayers = plugin.getApi().getServerToPlayers(); - Multimap human = HashMultimap.create(); - for (Map.Entry 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 serverToPlayers = plugin.getApi().getServerToPlayers(); + Multimap 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 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 serverToPlayers = plugin.getApi().getServerToPlayers(); - Multimap human = HashMultimap.create(); - for (Map.Entry 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 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 serverToPlayers = plugin.getApi().getServerToPlayers(); + Multimap 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"); } } - */ } \ No newline at end of file