diff --git a/pom.xml b/pom.xml
index 39457bd..f31b3c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,7 +112,7 @@
redis.clients
jedis
- 2.0.0
+ 2.2.1
net.md-5
diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
index cb6293d..e5a5475 100644
--- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
+++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
@@ -7,7 +7,10 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Joiner;
-import com.google.common.collect.*;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
@@ -21,31 +24,48 @@ import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
+import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.event.EventHandler;
import org.apache.commons.lang3.time.FastDateFormat;
import org.yaml.snakeyaml.Yaml;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.JedisPubSub;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+import redis.clients.jedis.exceptions.JedisException;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
+/**
+ * The RedisBungee plugin.
+ *
+ * The only function of interest is {@link #getApi()}, which exposes some functions in this class.
+ */
public class RedisBungee extends Plugin implements Listener {
private static final ServerPing.PlayerInfo[] EMPTY_PLAYERINFO = new ServerPing.PlayerInfo[]{};
- private static JedisPool pool;
- private static String serverId;
- private static List servers = Lists.newArrayList();
- private static RedisBungee plugin;
+ private RedisBungeeCommandSender commandSender = new RedisBungeeCommandSender();
+ private JedisPool pool;
+ private String serverId;
+ private List servers = Lists.newArrayList();
+ private RedisBungee plugin;
+ private static RedisBungeeAPI api;
+ private PubSubListener psl;
+ private ScheduledTask pubSubTask;
private boolean canonicalGlist = true;
/**
- * Get a combined count of all players on this network.
+ * Fetch the {@link RedisBungeeAPI} object created on plugin start.
*
- * @return a count of all players found
+ * @return the {@link RedisBungeeAPI} object
*/
- public static int getCount() {
+ public static RedisBungeeAPI getApi() {
+ return api;
+ }
+
+ public int getCount() {
Jedis rsc = pool.getResource();
int c = 0;
try {
@@ -61,14 +81,7 @@ public class RedisBungee extends Plugin implements Listener {
return c;
}
- /**
- * Get a combined list of players on this network.
- *
- * Note that this function returns an immutable {@link java.util.Set}.
- *
- * @return a Set with all players found
- */
- public static Set getPlayers() {
+ public Set getPlayers() {
Set players = new HashSet<>();
for (ProxiedPlayer pp : plugin.getProxy().getPlayers()) {
players.add(pp.getName());
@@ -91,14 +104,7 @@ public class RedisBungee extends Plugin implements Listener {
return ImmutableSet.copyOf(players);
}
- /**
- * Get the server where the specified player is playing. This function also deals with the case of local players
- * as well, and will return local information on them.
- *
- * @param name a player name
- * @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on.
- */
- public static ServerInfo getServerFor(String name) {
+ public ServerInfo getServerFor(String name) {
ServerInfo server = null;
if (plugin.getProxy().getPlayer(name) != null) return plugin.getProxy().getPlayer(name).getServer().getInfo();
if (pool != null) {
@@ -113,18 +119,11 @@ public class RedisBungee extends Plugin implements Listener {
return server;
}
- private static long getUnixTimestamp() {
+ private long getUnixTimestamp() {
return TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
- /**
- * Get the last time a player was on. If the player is currently online, this will return 0. If the player has not been recorded,
- * this will return -1. Otherwise it will return a value in seconds.
- *
- * @param name a player name
- * @return the last time a player was on, if online returns a 0
- */
- public static long getLastOnline(String name) {
+ public long getLastOnline(String name) {
long time = -1L;
if (plugin.getProxy().getPlayer(name) != null) return 0;
if (pool != null) {
@@ -231,18 +230,25 @@ public class RedisBungee extends Plugin implements Listener {
}
});
getProxy().getPluginManager().registerListener(this, this);
+ api = new RedisBungeeAPI(this);
+ psl = new PubSubListener();
+ pubSubTask = getProxy().getScheduler().runAsync(this, psl);
}
}
@Override
public void onDisable() {
if (pool != null) {
+ // Poison the PubSub listener
+ psl.poison();
+ getProxy().getScheduler().cancel(this);
Jedis tmpRsc = pool.getResource();
try {
tmpRsc.set("server:" + serverId + ":playerCount", "0"); // reset
for (String i : tmpRsc.smembers("server:" + serverId + ":usersOnline")) {
tmpRsc.srem("server:" + serverId + ":usersOnline", i);
}
+ } catch (JedisException | ClassCastException ignored) {
} finally {
pool.returnResource(tmpRsc);
}
@@ -354,4 +360,58 @@ public class RedisBungee extends Plugin implements Listener {
reply.setVersion(old.getVersion());
event.setResponse(reply);
}
+
+ private class PubSubListener implements Runnable {
+
+ private Jedis rsc;
+ private JedisPubSubHandler jpsh;
+
+ @Override
+ public void run() {
+ try {
+ rsc = pool.getResource();
+ jpsh = new JedisPubSubHandler();
+ rsc.subscribe(jpsh, "redisbungee-" + serverId, "redisbungee-allservers");
+ } catch (JedisException | ClassCastException ignored) {
+ }
+ }
+
+ public void poison() {
+ jpsh.unsubscribe();
+ pool.returnResource(rsc);
+ }
+ }
+
+ private class JedisPubSubHandler extends JedisPubSub {
+ @Override
+ public void onMessage(String s, String s2) {
+ String cmd;
+ if (s2.startsWith("/")) {
+ cmd = s2.substring(1);
+ } else {
+ cmd = s2;
+ }
+ getProxy().getPluginManager().dispatchCommand(commandSender, cmd);
+ }
+
+ @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) {
+ }
+ }
}
diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java
new file mode 100644
index 0000000..dfcdc06
--- /dev/null
+++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2013 tuxed
+ * This work is free. You can redistribute it and/or modify it under the
+ * terms of the Do What The Fuck You Want To Public License, Version 2,
+ * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
+ */
+package com.imaginarycode.minecraft.redisbungee;
+
+import net.md_5.bungee.api.config.ServerInfo;
+
+import java.util.Set;
+
+/**
+ * This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}.
+ *
+ * @author tuxed
+ * @since 0.2.3
+ */
+public class RedisBungeeAPI {
+ private RedisBungee plugin;
+
+ protected RedisBungeeAPI(RedisBungee plugin) {
+ this.plugin = plugin;
+ }
+
+ /**
+ * Get a combined count of all players on this network.
+ *
+ * @return a count of all players found
+ */
+ public int getPlayerCount() {
+ return plugin.getCount();
+ }
+
+ /**
+ * Get the last time a player was on. If the player is currently online, this will return 0. If the player has not been recorded,
+ * this will return -1. Otherwise it will return a value in seconds.
+ *
+ * @param player a player name
+ * @return the last time a player was on, if online returns a 0
+ */
+ public long getLastOnline(String player) {
+ return plugin.getLastOnline(player);
+ }
+
+ /**
+ * Get the server where the specified player is playing. This function also deals with the case of local players
+ * as well, and will return local information on them.
+ *
+ * @param player a player name
+ * @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on.
+ */
+ public ServerInfo getServerFor(String player) {
+ return plugin.getServerFor(player);
+ }
+
+ /**
+ * Get a combined list of players on this network.
+ *
+ * Note that this function returns an immutable {@link java.util.Set}.
+ *
+ * @return a Set with all players found
+ */
+ public Set getPlayersOnline() {
+ return plugin.getPlayers();
+ }
+}
diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java
new file mode 100644
index 0000000..c70b1f1
--- /dev/null
+++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright © 2013 tuxed
+ * This work is free. You can redistribute it and/or modify it under the
+ * terms of the Do What The Fuck You Want To Public License, Version 2,
+ * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
+ */
+package com.imaginarycode.minecraft.redisbungee;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+
+import java.util.Collection;
+
+/**
+ * This class is the CommandSender that RedisBungee uses to dispatch commands to BungeeCord.
+ *
+ * It inherits all permissions of the console command sender. Sending messages and modifying permissions are no-ops.
+ *
+ * @author tuxed
+ * @since 0.2.3
+ */
+public class RedisBungeeCommandSender implements CommandSender {
+ @Override
+ public String getName() {
+ return "RedisBungee";
+ }
+
+ @Override
+ public void sendMessage(String s) {
+ // no-op
+ }
+
+ @Override
+ public void sendMessages(String... strings) {
+ // no-op
+ }
+
+ @Override
+ public Collection getGroups() {
+ return ProxyServer.getInstance().getConsole().getGroups();
+ }
+
+ @Override
+ public void addGroups(String... strings) {
+ // no-op
+ }
+
+ @Override
+ public void removeGroups(String... strings) {
+ // no-op
+ }
+
+ @Override
+ public boolean hasPermission(String s) {
+ return ProxyServer.getInstance().getConsole().hasPermission(s);
+ }
+
+ @Override
+ public void setPermission(String s, boolean b) {
+ // no-op
+ }
+}