diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java index 6762708..751f132 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java @@ -6,29 +6,25 @@ */ package com.imaginarycode.minecraft.redisbungee; -import com.google.common.base.Joiner; 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.ByteArrayDataInput; -import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; +import lombok.Getter; import lombok.NonNull; -import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.api.event.*; -import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.YamlConfiguration; -import net.md_5.bungee.event.EventHandler; -import redis.clients.jedis.*; +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; @@ -51,9 +47,10 @@ import static com.google.common.base.Preconditions.checkArgument; *

* The only function of interest is {@link #getApi()}, which exposes some functions in this class. */ -public final class RedisBungee extends Plugin implements Listener { +public final class RedisBungee extends Plugin { private static Configuration configuration; - private static JedisPool pool; + @Getter + private JedisPool pool; private static RedisBungeeAPI api; private static PubSubListener psl = null; private List serverIds; @@ -303,7 +300,7 @@ public final class RedisBungee extends Plugin implements Listener { } } if (!found) - cleanUpPlayer(member, tmpRsc); + RedisUtil.cleanUpPlayer(member, tmpRsc); else tmpRsc.srem("server:" + configuration.getString("server-id") + ":usersOnline", member); } @@ -337,7 +334,7 @@ public final class RedisBungee extends Plugin implements Listener { getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand()); getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll()); getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId()); - getProxy().getPluginManager().registerListener(this, this); + getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this)); api = new RedisBungeeAPI(this); psl = new PubSubListener(); getProxy().getScheduler().runAsync(this, psl); @@ -360,7 +357,7 @@ public final class RedisBungee extends Plugin implements Listener { } } if (!found) { - cleanUpPlayer(member, tmpRsc); + RedisUtil.cleanUpPlayer(member, tmpRsc); getLogger().warning("Player found in set that was not found locally and globally: " + member); } else { tmpRsc.srem("server:" + configuration.getString("server-id") + ":usersOnline", member); @@ -386,7 +383,7 @@ public final class RedisBungee extends Plugin implements Listener { tmpRsc.set("server:" + configuration.getString("server-id") + ":playerCount", "0"); // reset if (tmpRsc.scard("server:" + configuration.getString("server-id") + ":usersOnline") > 0) { for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline")) - cleanUpPlayer(member, tmpRsc); + RedisUtil.cleanUpPlayer(member, tmpRsc); } tmpRsc.srem("servers", configuration.getString("server-id")); } finally { @@ -466,176 +463,6 @@ public final class RedisBungee extends Plugin implements Listener { } } - @EventHandler - public void onPreLogin(PreLoginEvent event) { - if (pool != null) { - Jedis rsc = pool.getResource(); - try { - for (String server : getServerIds()) { - if (rsc.sismember("server:" + server + ":usersOnline", event.getConnection().getName())) { - event.setCancelled(true); - event.setCancelReason("You are already logged on to this server."); - break; - } - } - } finally { - pool.returnResource(rsc); - } - } - } - - @EventHandler - public void onPlayerConnect(final PostLoginEvent event) { - if (pool != null) { - getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() { - @Override - public void run() { - Jedis rsc = pool.getResource(); - try { - rsc.sadd("server:" + configuration.getString("server-id", "") + ":usersOnline", event.getPlayer().getName()); - rsc.hset("player:" + event.getPlayer().getName(), "online", "0"); - rsc.hset("player:" + event.getPlayer().getName(), "ip", event.getPlayer().getAddress().getAddress().getHostAddress()); - rsc.hset("player:" + event.getPlayer().getName(), "name", event.getPlayer().getName()); - } finally { - pool.returnResource(rsc); - } - } - }); - } - // I used to have a task that eagerly waited for the user to be connected. - // Well, upon further inspection of BungeeCord's source code, this turned - // out to not be needed at all, since ServerConnectedEvent is called anyway. - } - - @EventHandler - public void onPlayerDisconnect(final PlayerDisconnectEvent event) { - if (pool != null) { - getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() { - @Override - public void run() { - Jedis rsc = pool.getResource(); - try { - rsc.hset("player:" + event.getPlayer().getName(), "online", String.valueOf(System.currentTimeMillis())); - cleanUpPlayer(event.getPlayer().getName(), rsc); - } finally { - pool.returnResource(rsc); - } - } - }); - } - } - - @EventHandler - public void onServerChange(final ServerConnectedEvent event) { - if (pool != null) { - getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() { - @Override - public void run() { - Jedis rsc = pool.getResource(); - try { - rsc.hset("player:" + event.getPlayer().getName(), "server", event.getServer().getInfo().getName()); - } finally { - pool.returnResource(rsc); - } - } - }); - } - } - - @EventHandler - public void onPing(ProxyPingEvent event) { - ServerPing old = event.getResponse(); - ServerPing reply = new ServerPing(); - if (configuration.getBoolean("player-list-in-ping", false)) { - Set players = getPlayers(); - ServerPing.PlayerInfo[] info = new ServerPing.PlayerInfo[players.size()]; - int idx = 0; - for (String player : players) { - info[idx] = new ServerPing.PlayerInfo(player, ""); - idx++; - } - reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), players.size(), info)); - } else { - reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), getCount(), null)); - } - reply.setDescription(old.getDescription()); - reply.setFavicon(old.getFaviconObject()); - reply.setVersion(old.getVersion()); - event.setResponse(reply); - } - - @EventHandler - public void onPluginMessage(PluginMessageEvent event) { - if (event.getTag().equals("RedisBungee") && event.getSender() instanceof Server) { - ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); - - String subchannel = in.readUTF(); - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - String type; - - switch (subchannel) { - case "PlayerList": - out.writeUTF("Players"); - Set source = Collections.emptySet(); - type = in.readUTF(); - if (type.equals("ALL")) { - out.writeUTF("ALL"); - source = getPlayers(); - } else { - try { - source = getPlayersOnServer(type); - } catch (IllegalArgumentException ignored) { - } - } - out.writeUTF(Joiner.on(',').join(source)); - break; - case "PlayerCount": - out.writeUTF("PlayerCount"); - type = in.readUTF(); - if (type.equals("ALL")) { - out.writeUTF("ALL"); - out.writeInt(getCurrentCount()); - } else { - out.writeUTF(type); - try { - out.writeInt(getPlayersOnServer(type).size()); - } catch (IllegalArgumentException e) { - out.writeInt(0); - } - } - out.writeInt(getCurrentCount()); - break; - case "LastOnline": - String user = in.readUTF(); - out.writeUTF("LastOnline"); - out.writeUTF(user); - out.writeLong(getLastOnline(user)); - break; - default: - break; - } - - ((Server) event.getSender()).sendData("RedisBungee", out.toByteArray()); - } - } - - @EventHandler - public void onPubSubMessage(PubSubMessageEvent event) { - if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + configuration.getString("server-id"))) { - String message = event.getMessage(); - if (message.startsWith("/")) - message = message.substring(1); - getLogger().info("Invoking command via PubSub: /" + message); - getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.instance, message); - } - } - - private void cleanUpPlayer(String player, Jedis rsc) { - rsc.srem("server:" + configuration.getString("server-id") + ":usersOnline", player); - rsc.hdel("player:" + player, "server"); - rsc.hdel("player:" + player, "ip"); - } - class PubSubListener implements Runnable { private Jedis rsc; private JedisPubSubHandler jpsh; diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java new file mode 100644 index 0000000..be3f93a --- /dev/null +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java @@ -0,0 +1,186 @@ +package com.imaginarycode.minecraft.redisbungee; + +import com.google.common.base.Joiner; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; +import lombok.AllArgsConstructor; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.*; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import redis.clients.jedis.Jedis; + +import java.util.Collections; +import java.util.Set; + +@AllArgsConstructor +public class RedisBungeeListener implements Listener { + private final RedisBungee plugin; + + @EventHandler + public void onPreLogin(PreLoginEvent event) { + if (plugin.getPool() != null) { + Jedis rsc = plugin.getPool().getResource(); + try { + for (String server : plugin.getServerIds()) { + if (rsc.sismember("server:" + server + ":usersOnline", event.getConnection().getName())) { + event.setCancelled(true); + event.setCancelReason("You are already logged on to this server."); + break; + } + } + } finally { + plugin.getPool().returnResource(rsc); + } + } + } + + @EventHandler + public void onPlayerConnect(final PostLoginEvent event) { + if (plugin.getPool() != null) { + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + Jedis rsc = plugin.getPool().getResource(); + try { + rsc.sadd("server:" + RedisBungee.getConfiguration().getString("server-id", "") + ":usersOnline", event.getPlayer().getName()); + rsc.hset("player:" + event.getPlayer().getName(), "online", "0"); + rsc.hset("player:" + event.getPlayer().getName(), "ip", event.getPlayer().getAddress().getAddress().getHostAddress()); + rsc.hset("player:" + event.getPlayer().getName(), "name", event.getPlayer().getName()); + } finally { + plugin.getPool().returnResource(rsc); + } + } + }); + } + // I used to have a task that eagerly waited for the user to be connected. + // Well, upon further inspection of BungeeCord's source code, this turned + // out to not be needed at all, since ServerConnectedEvent is called anyway. + } + + @EventHandler + public void onPlayerDisconnect(final PlayerDisconnectEvent event) { + if (plugin.getPool() != null) { + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + Jedis rsc = plugin.getPool().getResource(); + try { + rsc.hset("player:" + event.getPlayer().getName(), "online", String.valueOf(System.currentTimeMillis())); + RedisUtil.cleanUpPlayer(event.getPlayer().getName(), rsc); + } finally { + plugin.getPool().returnResource(rsc); + } + } + }); + } + } + + @EventHandler + public void onServerChange(final ServerConnectedEvent event) { + if (plugin.getPool() != null) { + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + Jedis rsc = plugin.getPool().getResource(); + try { + rsc.hset("player:" + event.getPlayer().getName(), "server", event.getServer().getInfo().getName()); + } finally { + plugin.getPool().returnResource(rsc); + } + } + }); + } + } + + @EventHandler + public void onPing(ProxyPingEvent event) { + ServerPing old = event.getResponse(); + ServerPing reply = new ServerPing(); + if (RedisBungee.getConfiguration().getBoolean("player-list-in-ping", false)) { + Set players = plugin.getPlayers(); + ServerPing.PlayerInfo[] info = new ServerPing.PlayerInfo[players.size()]; + int idx = 0; + for (String player : players) { + info[idx] = new ServerPing.PlayerInfo(player, ""); + idx++; + } + reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), players.size(), info)); + } else { + reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), plugin.getCount(), null)); + } + reply.setDescription(old.getDescription()); + reply.setFavicon(old.getFaviconObject()); + reply.setVersion(old.getVersion()); + event.setResponse(reply); + } + + @EventHandler + public void onPluginMessage(PluginMessageEvent event) { + if (event.getTag().equals("RedisBungee") && event.getSender() instanceof Server) { + ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); + + String subchannel = in.readUTF(); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + String type; + + switch (subchannel) { + case "PlayerList": + out.writeUTF("Players"); + Set source = Collections.emptySet(); + type = in.readUTF(); + if (type.equals("ALL")) { + out.writeUTF("ALL"); + source = plugin.getPlayers(); + } else { + try { + source = plugin.getPlayersOnServer(type); + } catch (IllegalArgumentException ignored) { + } + } + out.writeUTF(Joiner.on(',').join(source)); + 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.getPlayersOnServer(type).size()); + } catch (IllegalArgumentException e) { + out.writeInt(0); + } + } + out.writeInt(plugin.getCurrentCount()); + break; + case "LastOnline": + String user = in.readUTF(); + out.writeUTF("LastOnline"); + out.writeUTF(user); + out.writeLong(plugin.getLastOnline(user)); + break; + default: + break; + } + + ((Server) event.getSender()).sendData("RedisBungee", out.toByteArray()); + } + } + + @EventHandler + public void onPubSubMessage(PubSubMessageEvent event) { + if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + RedisBungee.getConfiguration().getString("server-id"))) { + String message = event.getMessage(); + if (message.startsWith("/")) + message = message.substring(1); + plugin.getLogger().info("Invoking command via PubSub: /" + message); + plugin.getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.instance, message); + } + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisUtil.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisUtil.java new file mode 100644 index 0000000..cb13445 --- /dev/null +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisUtil.java @@ -0,0 +1,11 @@ +package com.imaginarycode.minecraft.redisbungee; + +import redis.clients.jedis.Jedis; + +public class RedisUtil { + public static void cleanUpPlayer(String player, Jedis rsc) { + rsc.srem("server:" + RedisBungee.getConfiguration().getString("server-id") + ":usersOnline", player); + rsc.hdel("player:" + player, "server"); + rsc.hdel("player:" + player, "ip"); + } +}