diff --git a/pom.xml b/pom.xml index aa30787..b9362cc 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,14 @@ redis.clients jedis - 2.4.2 + 2.6.0 + compile + + + org.projectlombok + lombok + 1.14.8 + provided \ No newline at end of file diff --git a/src/main/java/me/vemacs/executeeverywhere/bukkit/EECommand.java b/src/main/java/me/vemacs/executeeverywhere/bukkit/EECommand.java new file mode 100644 index 0000000..25af165 --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/bukkit/EECommand.java @@ -0,0 +1,63 @@ +package me.vemacs.executeeverywhere.bukkit; + +import com.google.common.base.Joiner; +import lombok.RequiredArgsConstructor; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.exceptions.JedisConnectionException; + +import java.util.Arrays; + +@RequiredArgsConstructor +public class EECommand implements CommandExecutor { + private final ExecuteEverywhere plugin; + + @Override + public boolean onCommand(final CommandSender commandSender, Command command, String s, String[] args) { + if (args.length < 1 || (command.getName().equalsIgnoreCase("eeg") && args.length < 2)) { + commandSender.sendMessage(ChatColor.RED + "Invalid usage."); + if (command.getName().equalsIgnoreCase("eec")) { + commandSender.sendMessage(ChatColor.RED + "/" + s + " "); + } else { + commandSender.sendMessage(ChatColor.RED + "/" + s + " "); + } + return true; + } + + final String channel; + final String run; + + if (command.getName().equals("eeg")) { + channel = "ee-" + args[0]; + run = Joiner.on(' ').join(Arrays.copyOfRange(args, 1, args.length)); + } else if (command.getName().equals("ee")) { + channel = "ee"; + run = Joiner.on(' ').join(args); + commandSender.sendMessage(ChatColor.GRAY + "(Assuming you want to run this command over all Bukkit servers.)"); + } else if (command.getName().equals("eb")) { + channel = "eb"; + run = Joiner.on(' ').join(args); + commandSender.sendMessage(ChatColor.GRAY + "(Assuming you want to run this command over all BungeeCord servers.)"); + } else { + // Shouldn't happen + return false; + } + + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() { + @Override + public void run() { + try (Jedis jedis = plugin.getPool().getResource()) { + jedis.publish(channel, run); + commandSender.sendMessage(ChatColor.GREEN + "Command successfully queued for execution."); + } catch (JedisConnectionException e) { + commandSender.sendMessage(ChatColor.RED + "Could not send the command! Please try again."); + } + } + }); + + return true; + } +} diff --git a/src/main/java/me/vemacs/executeeverywhere/bukkit/ExecuteEverywhere.java b/src/main/java/me/vemacs/executeeverywhere/bukkit/ExecuteEverywhere.java index 5a2578c..a964e3a 100644 --- a/src/main/java/me/vemacs/executeeverywhere/bukkit/ExecuteEverywhere.java +++ b/src/main/java/me/vemacs/executeeverywhere/bukkit/ExecuteEverywhere.java @@ -1,55 +1,41 @@ package me.vemacs.executeeverywhere.bukkit; -import com.google.common.base.Joiner; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; +import lombok.Getter; +import me.vemacs.executeeverywhere.common.AbstractJedisPubSub; +import me.vemacs.executeeverywhere.common.EEConfiguration; import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; -import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; -import redis.clients.jedis.JedisPubSub; import java.lang.Override; +import java.util.List; public class ExecuteEverywhere extends JavaPlugin implements Listener { + @Getter private JedisPool pool; - private static final Joiner joiner = Joiner.on(" "); - private final String CHANNEL = "ee"; - private final String BUNGEE_CHANNEL = "eb"; - private static Plugin instance; + @Getter + private EEConfiguration configuration; private EESubscriber eeSubscriber; @Override public void onEnable() { - instance = this; saveDefaultConfig(); + String ip = getConfig().getString("ip"); int port = getConfig().getInt("port"); String password = getConfig().getString("password"); - if (password == null || password.equals("")) - pool = new JedisPool(new JedisPoolConfig(), ip, port, 0); - else - pool = new JedisPool(new JedisPoolConfig(), ip, port, 0, password); - new BukkitRunnable() { - @Override - public void run() { - eeSubscriber = new EESubscriber(); - Jedis jedis = pool.getResource(); - try { - jedis.subscribe(eeSubscriber, CHANNEL); - } catch (Exception e) { - e.printStackTrace(); - pool.returnBrokenResource(jedis); - getLogger().severe("Unable to connect to Redis server."); - return; - } - pool.returnResource(jedis); - } - }.runTaskAsynchronously(this); + List serverGroups = getConfig().getStringList("groups"); + configuration = new EEConfiguration(serverGroups, ip, port, password); + + pool = configuration.getJedisPool(); + + getServer().getScheduler().runTaskAsynchronously(this, eeSubscriber = new EESubscriber()); + + EECommand command = new EECommand(this); + getCommand("ee").setExecutor(command); + getCommand("eb").setExecutor(command); + getCommand("eeg").setExecutor(command); } @Override @@ -58,66 +44,21 @@ public void onDisable() { pool.destroy(); } - @Override - public boolean onCommand(CommandSender sender, final Command cmd, String label, String[] args) { - if (args.length == 0) return false; - String cmdString = joiner.join(args); - if (cmdString.startsWith("/")) - cmdString = cmdString.substring(1); - final String finalCmdString = cmdString; - new BukkitRunnable() { - @Override - public void run() { - Jedis jedis = pool.getResource(); - try { - switch (cmd.getName().toLowerCase()) { - case "eb": - jedis.publish(BUNGEE_CHANNEL, finalCmdString); - break; - default: - jedis.publish(CHANNEL, finalCmdString); - } - } catch (Exception e) { - pool.returnBrokenResource(jedis); - } - pool.returnResource(jedis); - } - }.runTaskAsynchronously(this); - sender.sendMessage(ChatColor.GREEN + "Sent /" + cmdString + " for execution."); - return true; - } + public class EESubscriber extends AbstractJedisPubSub { + public EESubscriber() { + super(pool, configuration); + } - public class EESubscriber extends JedisPubSub { @Override public void onMessage(String channel, final String msg) { // Needs to be done in the server thread new BukkitRunnable() { @Override public void run() { - ExecuteEverywhere.instance.getLogger().info("Dispatching /" + msg); + getLogger().info("Dispatching /" + msg); getServer().dispatchCommand(getServer().getConsoleSender(), msg); } - }.runTaskAsynchronously(ExecuteEverywhere.instance); - } - - @Override - public void onPMessage(String s, String s2, String s3) { - } - - @Override - public void onSubscribe(String s, int i) { - } - - @Override - public void onUnsubscribe(String s, int i) { - } - - @Override - public void onPUnsubscribe(String s, int i) { - } - - @Override - public void onPSubscribe(String s, int i) { + }.runTaskAsynchronously(ExecuteEverywhere.this); } } } diff --git a/src/main/java/me/vemacs/executeeverywhere/bungee/EECommand.java b/src/main/java/me/vemacs/executeeverywhere/bungee/EECommand.java new file mode 100644 index 0000000..23224cc --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/bungee/EECommand.java @@ -0,0 +1,69 @@ +package me.vemacs.executeeverywhere.bungee; + +import com.google.common.base.Joiner; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.exceptions.JedisConnectionException; + +import java.util.Arrays; + +public class EECommand extends Command { + private final ExecuteEverywhere plugin; + private final EECommandKind kind; + + public EECommand(ExecuteEverywhere plugin, String name, EECommandKind kind) { + super(name, "executeeverywhere.use"); + this.kind = kind; + this.plugin = plugin; + } + + @Override + public void execute(final CommandSender commandSender, String[] args) { + if (args.length < 1 || (kind == EECommandKind.GROUP && args.length < 2)) { + commandSender.sendMessage(ChatColor.RED + "Invalid usage."); + if (kind == EECommandKind.GROUP) { + commandSender.sendMessage(ChatColor.RED + "/" + getName() + " "); + } else { + commandSender.sendMessage(ChatColor.RED + "/" + getName() + " "); + } + return; + } + + String group = args[0]; + final String channel; + final String run; + + switch (kind) { + case BUNGEECORD: + channel = "eb"; + run = Joiner.on(' ').join(args); + commandSender.sendMessage(ChatColor.GRAY + "(Assuming you want to run this command over all BungeeCord servers.)"); + break; + case BUKKIT: + channel = "ee"; + run = Joiner.on(' ').join(args); + commandSender.sendMessage(ChatColor.GRAY + "(Assuming you want to run this command over all Bukkit servers.)"); + break; + case GROUP: + channel = "ee-" + group; + run = Joiner.on(' ').join(Arrays.copyOfRange(args, 1, args.length)); + break; + default: + return; + } + + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + try (Jedis jedis = plugin.getPool().getResource()) { + jedis.publish(channel, run); + commandSender.sendMessage(ChatColor.GREEN + "Command successfully queued for execution."); + } catch (JedisConnectionException e) { + commandSender.sendMessage(ChatColor.RED + "Could not send the command! Please try again."); + } + } + }); + } +} diff --git a/src/main/java/me/vemacs/executeeverywhere/bungee/EECommandKind.java b/src/main/java/me/vemacs/executeeverywhere/bungee/EECommandKind.java new file mode 100644 index 0000000..5765f04 --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/bungee/EECommandKind.java @@ -0,0 +1,7 @@ +package me.vemacs.executeeverywhere.bungee; + +public enum EECommandKind { + BUNGEECORD, + BUKKIT, + GROUP +} diff --git a/src/main/java/me/vemacs/executeeverywhere/bungee/ExecuteEverywhere.java b/src/main/java/me/vemacs/executeeverywhere/bungee/ExecuteEverywhere.java index 82f1d46..8a62a0a 100644 --- a/src/main/java/me/vemacs/executeeverywhere/bungee/ExecuteEverywhere.java +++ b/src/main/java/me/vemacs/executeeverywhere/bungee/ExecuteEverywhere.java @@ -1,6 +1,9 @@ package me.vemacs.executeeverywhere.bungee; import com.google.common.io.ByteStreams; +import lombok.Getter; +import me.vemacs.executeeverywhere.common.AbstractJedisPubSub; +import me.vemacs.executeeverywhere.common.EEConfiguration; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; @@ -13,45 +16,42 @@ import redis.clients.jedis.JedisPubSub; import java.io.*; +import java.util.List; -public class ExecuteEverywhere extends Plugin implements Listener { +public class ExecuteEverywhere extends Plugin { + @Getter private JedisPool pool; - private final String BUNGEE_CHANNEL = "eb"; - private static Plugin instance; - - Configuration config; - EESubscriber eeSubscriber; + @Getter + private EEConfiguration configuration; + private EESubscriber eeSubscriber; @Override public void onEnable() { - instance = this; + final Configuration config; try { config = ConfigurationProvider.getProvider(YamlConfiguration.class).load( loadResource(this, "config.yml")); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException("Unable to read config.yml", e); } - final String ip = config.getString("ip"); - final int port = config.getInt("port"); - final String password = config.getString("password"); + + String ip = config.getString("ip"); + int port = config.getInt("port"); + String password = config.getString("password"); + List serverGroups = config.getStringList("groups"); + configuration = new EEConfiguration(serverGroups, ip, port, password); + getProxy().getScheduler().runAsync(this, new Runnable() { @Override public void run() { - if (password == null || password.equals("")) - pool = new JedisPool(new JedisPoolConfig(), ip, port, 0); - else - pool = new JedisPool(new JedisPoolConfig(), ip, port, 0, password); - Jedis jedis = pool.getResource(); - try { - jedis.subscribe(new EESubscriber(), BUNGEE_CHANNEL); - } catch (Exception e) { - e.printStackTrace(); - pool.returnBrokenResource(jedis); - getLogger().severe("Unable to connect to Redis server."); - return; - } - pool.returnResource(jedis); } + pool = configuration.getJedisPool(); + getProxy().getScheduler().runAsync(ExecuteEverywhere.this, eeSubscriber = new EESubscriber()); + } }); + + getProxy().getPluginManager().registerCommand(this, new EECommand(this, "ee", EECommandKind.BUKKIT)); + getProxy().getPluginManager().registerCommand(this, new EECommand(this, "eb", EECommandKind.BUNGEECORD)); + getProxy().getPluginManager().registerCommand(this, new EECommand(this, "eeg", EECommandKind.GROUP)); } @Override @@ -60,32 +60,15 @@ public void onDisable() { pool.destroy(); } - public class EESubscriber extends JedisPubSub { - @Override - public void onMessage(String channel, final String msg) { - ExecuteEverywhere.instance.getLogger().info("Dispatching /" + msg); - ProxyServer ps = ProxyServer.getInstance(); - ps.getPluginManager().dispatchCommand(ps.getConsole(), msg); - } - - @Override - public void onPMessage(String s, String s2, String s3) { - } - - @Override - public void onSubscribe(String s, int i) { + public class EESubscriber extends AbstractJedisPubSub { + public EESubscriber() { + super(pool, configuration); } @Override - public void onUnsubscribe(String s, int i) { - } - - @Override - public void onPUnsubscribe(String s, int i) { - } - - @Override - public void onPSubscribe(String s, int i) { + public void onMessage(String channel, final String msg) { + ExecuteEverywhere.this.getLogger().info("Dispatching /" + msg); + ProxyServer.getInstance().getPluginManager().dispatchCommand(ProxyServer.getInstance().getConsole(), msg); } } diff --git a/src/main/java/me/vemacs/executeeverywhere/common/AbstractJedisPubSub.java b/src/main/java/me/vemacs/executeeverywhere/common/AbstractJedisPubSub.java new file mode 100644 index 0000000..155c34b --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/common/AbstractJedisPubSub.java @@ -0,0 +1,44 @@ +package me.vemacs.executeeverywhere.common; + +import lombok.RequiredArgsConstructor; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPubSub; + +@RequiredArgsConstructor +public abstract class AbstractJedisPubSub extends JedisPubSub implements Runnable { + private final JedisPool pool; + private final EEConfiguration configuration; + + @Override + public void run() { + try (Jedis jedis = pool.getResource()) { + jedis.subscribe(this, configuration.getRedisChannels()); + } + } + + @Override + public void onPMessage(String s, String s1, String s2) { + + } + + @Override + public void onSubscribe(String s, int i) { + + } + + @Override + public void onUnsubscribe(String s, int i) { + + } + + @Override + public void onPUnsubscribe(String s, int i) { + + } + + @Override + public void onPSubscribe(String s, int i) { + + } +} diff --git a/src/main/java/me/vemacs/executeeverywhere/common/EEConfiguration.java b/src/main/java/me/vemacs/executeeverywhere/common/EEConfiguration.java new file mode 100644 index 0000000..5691d42 --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/common/EEConfiguration.java @@ -0,0 +1,41 @@ +package me.vemacs.executeeverywhere.common; + +import com.google.common.collect.ObjectArrays; +import lombok.Data; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +import java.util.List; + +@Data +public class EEConfiguration { + private final List groups; + private final String redisHost; + private final int redisPort; + private final String redisPassword; + + public String[] getRedisChannels() { + String[] channels = groups.toArray(new String[groups.size()]); + for (int i = 0; i < channels.length; i++) { + channels[i] = "ee-" + channels[i]; + } + + // For legacy EE compatibility, we add the old "ee" and "eb" channels. + if (EEPlatform.BUKKIT.current()) { + channels = ObjectArrays.concat("ee", channels); + channels = ObjectArrays.concat("ee-bukkit", channels); + } else if (EEPlatform.BUNGEECORD.current()) { + channels = ObjectArrays.concat("eb", channels); + channels = ObjectArrays.concat("ee-bungeecord", channels); + } + + return channels; + } + + public JedisPool getJedisPool() { + if (redisPassword == null || redisPassword.isEmpty()) + return new JedisPool(new JedisPoolConfig(), redisHost, redisPort, 0); + else + return new JedisPool(new JedisPoolConfig(), redisHost, redisPort, 0, redisPassword); + } +} diff --git a/src/main/java/me/vemacs/executeeverywhere/common/EEPlatform.java b/src/main/java/me/vemacs/executeeverywhere/common/EEPlatform.java new file mode 100644 index 0000000..405dd81 --- /dev/null +++ b/src/main/java/me/vemacs/executeeverywhere/common/EEPlatform.java @@ -0,0 +1,28 @@ +package me.vemacs.executeeverywhere.common; + +public enum EEPlatform { + BUKKIT { + @Override + public boolean current() { + try { + Class.forName("org.bukkit.Bukkit"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + }, + BUNGEECORD { + @Override + public boolean current() { + try { + Class.forName("net.md_5.bungee.api.ProxyServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + }; + + public abstract boolean current(); +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b89f011..5ec90a5 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,6 @@ ip: 127.0.0.1 port: 6379 -password: "" \ No newline at end of file +password: "" +# Example groups: +# groups: [all, debian7, mybox1, mysql] +groups: [] \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 91d783b..abb4eb9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -15,6 +15,12 @@ commands: permission: executeeverywhere.use usage: /eb permission-message: You don't have permission to use this. + eeg: + description: Dispatches a command for execution to all connected servers in a group. + aliases: [executegroup] + permission: executeeverywhere.use + usage: /eeg + permission-message: You don't have permission to use this. permissions: executeeverywhere.use: description: Lets you dispatch a command for execution on all servers.