From 61dec7f03c478baa68055a6425bc77e730ed80cb Mon Sep 17 00:00:00 2001 From: mohammed jasem alaajel Date: Wed, 13 Apr 2022 17:14:08 +0400 Subject: [PATCH] seperate the internals from bungeecord api for easily supporting platform | progress 60% --- RedisBungee-API/pom.xml | 22 + .../minecraft/redisbungee/RedisBungeeAPI.java | 42 +- .../events/PubSubMessageEvent.java | 27 + .../internal/AbstractRedisBungeeListener.java | 70 +++ .../redisbungee/internal}/DataManager.java | 191 +++--- .../internal/JedisPubSubHandler.java | 47 ++ .../redisbungee/internal/PubSubListener.java | 71 +++ .../internal/RedisBungeeConfiguration.java | 36 ++ .../internal/RedisBungeePlugin.java | 79 +++ .../redisbungee/internal/RedisUtil.java | 51 ++ .../redisbungee/internal}/util/IOUtil.java | 6 +- .../internal}/util/LuaManager.java | 30 +- .../internal}/util/RedisCallable.java | 19 +- .../internal}/util/uuid/NameFetcher.java | 17 +- .../internal}/util/uuid/UUIDFetcher.java | 16 +- .../internal}/util/uuid/UUIDTranslator.java | 75 ++- RedisBungee-Bungee/pom.xml | 110 ++++ .../minecraft/redisbungee/RBUtils.java | 39 ++ .../redisbungee/RedisBungeeListener.java | 135 ++++ .../events/bungee}/PubSubMessageEvent.java | 12 +- pom.xml | 94 +-- .../minecraft/redisbungee/RedisBungee.java | 595 ------------------ .../redisbungee/RedisBungeeCommandSender.java | 77 --- .../redisbungee/RedisBungeeCommands.java | 356 ----------- .../redisbungee/RedisBungeeConfiguration.java | 44 -- .../redisbungee/RedisBungeeListener.java | 291 --------- .../minecraft/redisbungee/RedisUtil.java | 76 --- .../PlayerChangedServerNetworkEvent.java | 40 -- .../events/PlayerJoinedNetworkEvent.java | 28 - .../events/PlayerLeftNetworkEvent.java | 28 - src/main/resources/example_config.yml | 40 -- src/main/resources/lua/get_player_count.lua | 24 - src/main/resources/lua/server_to_players.lua | 18 - src/main/resources/plugin.yml | 9 - .../redisbungee/test/RedisUtilTest.java | 20 - .../redisbungee/test/UUIDNameTest.java | 47 -- 36 files changed, 945 insertions(+), 1937 deletions(-) create mode 100644 RedisBungee-API/pom.xml rename {src => RedisBungee-API/src}/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java (89%) create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractRedisBungeeListener.java rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/DataManager.java (57%) create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/JedisPubSubHandler.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeeConfiguration.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisUtil.java rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/IOUtil.java (74%) rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/LuaManager.java (58%) rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/RedisCallable.java (69%) rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/uuid/NameFetcher.java (76%) rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/uuid/UUIDFetcher.java (82%) rename {src/main/java/com/imaginarycode/minecraft/redisbungee => RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal}/util/uuid/UUIDTranslator.java (77%) create mode 100644 RedisBungee-Bungee/pom.xml create mode 100644 RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RBUtils.java create mode 100644 RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java rename {src/main/java/com/imaginarycode/minecraft/redisbungee/events => RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/events/bungee}/PubSubMessageEvent.java (69%) delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommands.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConfiguration.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/RedisUtil.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java delete mode 100644 src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java delete mode 100644 src/main/resources/example_config.yml delete mode 100644 src/main/resources/lua/get_player_count.lua delete mode 100644 src/main/resources/lua/server_to_players.lua delete mode 100644 src/main/resources/plugin.yml delete mode 100644 src/test/java/com/imaginarycode/minecraft/redisbungee/test/RedisUtilTest.java delete mode 100644 src/test/java/com/imaginarycode/minecraft/redisbungee/test/UUIDNameTest.java diff --git a/RedisBungee-API/pom.xml b/RedisBungee-API/pom.xml new file mode 100644 index 0000000..74411f1 --- /dev/null +++ b/RedisBungee-API/pom.xml @@ -0,0 +1,22 @@ + + + + RedisBungee + com.imaginarycode.minecraft + 0.7.0-SNAPSHOT + + 4.0.0 + + RedisBungee-API + + + + + + 8 + 8 + + + \ No newline at end of file diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java similarity index 89% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java index d5d3310..6dc6316 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java @@ -4,31 +4,34 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; -import lombok.NonNull; -import net.md_5.bungee.api.config.ServerInfo; +import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; +import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; +import org.checkerframework.checker.nullness.qual.NonNull; +import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import java.net.InetAddress; import java.util.*; /** - * This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}. + * This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungeePlugin#getApi()}. * * @author tuxed * @since 0.2.3 + * */ @SuppressWarnings("unused") public class RedisBungeeAPI { - private final RedisBungee plugin; + private final RedisBungeePlugin plugin; private final List reservedChannels; private static RedisBungeeAPI redisBungeeApi; - RedisBungeeAPI(RedisBungee plugin) { + RedisBungeeAPI(RedisBungeePlugin plugin) { this.plugin = plugin; redisBungeeApi = this; this.reservedChannels = ImmutableList.of( "redisbungee-allservers", - "redisbungee-" + RedisBungee.getConfiguration().getServerId(), + "redisbungee-" + plugin.getConfiguration().getServerId(), "redisbungee-data" ); } @@ -58,11 +61,10 @@ public class RedisBungeeAPI { * 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. + * @return a String name for the server the player is on. */ - public final ServerInfo getServerFor(@NonNull UUID player) { - String server = plugin.getDataManager().getServer(player); - return plugin.getProxy().getServerInfo(server); + public final String getServerFor(@NonNull UUID player) { + return plugin.getDataManager().getServer(player); } /** @@ -178,7 +180,7 @@ public class RedisBungeeAPI { } /** - * Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} to fire. + * Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for {@link PubSubMessageEvent} to fire. * * @param channel The PubSub channel * @param message the message body to send @@ -196,7 +198,7 @@ public class RedisBungeeAPI { * @since 0.2.5 */ public final String getServerId() { - return RedisBungee.getConfiguration().getServerId(); + return plugin.getConfiguration().getServerId(); } /** @@ -211,13 +213,13 @@ public class RedisBungeeAPI { } /** - * Register (a) PubSub channel(s), so that you may handle {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} for it. + * Register (a) PubSub channel(s), so that you may handle {@link PubSubMessageEvent} for it. * * @param channels the channels to register * @since 0.3 */ public final void registerPubSubChannels(String... channels) { - RedisBungee.getPubSubListener().addChannel(channels); + plugin.getPubSubListener().addChannel(channels); } /** @@ -231,7 +233,7 @@ public class RedisBungeeAPI { Preconditions.checkArgument(!reservedChannels.contains(channel), "attempting to unregister internal channel"); } - RedisBungee.getPubSubListener().removeChannel(channels); + plugin.getPubSubListener().removeChannel(channels); } /** @@ -302,18 +304,16 @@ public class RedisBungeeAPI { } /** - * This gets Redis Bungee Jedis pool + * This gives you instance of Jedis! * * @return {@link JedisPool} - * @since 0.6.5 + * @since 0.7.0 */ - public JedisPool getJedisPool() { - return this.plugin.getPool(); + public Jedis getJedisPool() { + return this.plugin.requestJedis(); } /** - * This alternative to {@link RedisBungee#getApi()} - * which now deprecated. but to maintain old plugins compatibility it won't be removed. * * @return the API instance. * @since 0.6.5 diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java new file mode 100644 index 0000000..e336d64 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java @@ -0,0 +1,27 @@ +package com.imaginarycode.minecraft.redisbungee.events; + +/** + * This event is posted when a PubSub message is received. + *

+ * Warning: This event is fired in a separate thread! + * + * @since 0.2.6 + */ + +public class PubSubMessageEvent { + private final String channel; + private final String message; + + public PubSubMessageEvent(String channel, String message) { + this.channel = channel; + this.message = message; + } + + public String getChannel() { + return channel; + } + + public String getMessage() { + return message; + } +} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractRedisBungeeListener.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractRedisBungeeListener.java new file mode 100644 index 0000000..3370f4e --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractRedisBungeeListener.java @@ -0,0 +1,70 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; +import com.google.common.io.ByteArrayDataOutput; +import com.google.gson.Gson; + +import java.net.InetAddress; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public abstract class AbstractRedisBungeeListener { + + protected static final String ALREADY_LOGGED_IN = "§cYou are already logged on to this server. \n\nIt may help to try logging in again in a few minutes.\nIf this does not resolve your issue, please contact staff."; + + protected static final String ONLINE_MODE_RECONNECT = "§cWhoops! You need to reconnect\n\nWe found someone online using your username. They were kicked and you may reconnect.\nIf this does not work, please contact staff."; + + protected final RedisBungeePlugin plugin; + protected final List exemptAddresses; + protected final Gson gson = new Gson(); + + public AbstractRedisBungeeListener(RedisBungeePlugin plugin, List exemptAddresses) { + this.plugin = plugin; + this.exemptAddresses = exemptAddresses; + } + + public abstract void onLogin(LE event); + + public abstract void onPostLogin(PLE event); + + public abstract void onPlayerDisconnect(PD event); + + public abstract void onServerChange(SC event); + + public abstract void onPing(PP event); + + public abstract void onPluginMessage(PM event); + + private void serializeMultiset(Multiset collection, ByteArrayDataOutput output) { + output.writeInt(collection.elementSet().size()); + for (Multiset.Entry entry : collection.entrySet()) { + output.writeUTF(entry.getElement()); + output.writeInt(entry.getCount()); + } + } + + @SuppressWarnings("SameParameterValue") + private void serializeMultimap(Multimap collection, boolean includeNames, ByteArrayDataOutput output) { + output.writeInt(collection.keySet().size()); + for (Map.Entry> entry : collection.asMap().entrySet()) { + output.writeUTF(entry.getKey()); + if (includeNames) { + serializeCollection(entry.getValue(), output); + } else { + output.writeInt(entry.getValue().size()); + } + } + } + + private void serializeCollection(Collection collection, ByteArrayDataOutput output) { + output.writeInt(collection.size()); + for (Object o : collection) { + output.writeUTF(o.toString()); + } + } + + public abstract void onPubSubMessage(PS event); +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/DataManager.java similarity index 57% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/DataManager.java index b063a3a..298aa67 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/DataManager.java @@ -1,23 +1,14 @@ -package com.imaginarycode.minecraft.redisbungee; +package com.imaginarycode.minecraft.redisbungee.internal; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.net.InetAddresses; +import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; -import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent; -import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent; -import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.event.EventHandler; import redis.clients.jedis.Jedis; import java.net.InetAddress; @@ -26,21 +17,21 @@ import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; /** * This class manages all the data that RedisBungee fetches from Redis, along with updates to that data. * * @since 0.3.3 */ -public class DataManager implements Listener { - private final RedisBungee plugin; +public abstract class DataManager { + private final RedisBungeePlugin

plugin; private final Cache serverCache = createCache(); private final Cache proxyCache = createCache(); private final Cache ipCache = createCache(); private final Cache lastOnlineCache = createCache(); + private final Gson gson = new Gson(); - public DataManager(RedisBungee plugin) { + public DataManager(RedisBungeePlugin

plugin) { this.plugin = plugin; } @@ -55,16 +46,16 @@ public class DataManager implements Listener { private final JsonParser parser = new JsonParser(); public String getServer(final UUID uuid) { - ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); + P player = plugin.getPlayer(uuid); if (player != null) - return player.getServer() != null ? player.getServer().getInfo().getName() : null; + return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null; try { return serverCache.get(uuid, new Callable() { @Override public String call() throws Exception { - try (Jedis tmpRsc = plugin.getPool().getResource()) { + try (Jedis tmpRsc = plugin.requestJedis()) { return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found"); } } @@ -72,22 +63,23 @@ public class DataManager implements Listener { } catch (ExecutionException | UncheckedExecutionException e) { if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) return null; // HACK - plugin.getLogger().log(Level.SEVERE, "Unable to get server", e); + plugin.logFatal("Unable to get server"); throw new RuntimeException("Unable to get server for " + uuid, e); } } + public String getProxy(final UUID uuid) { - ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); + P player = plugin.getPlayer(uuid); if (player != null) - return RedisBungee.getConfiguration().getServerId(); + return plugin.getConfiguration().getServerId(); try { return proxyCache.get(uuid, new Callable() { @Override public String call() throws Exception { - try (Jedis tmpRsc = plugin.getPool().getResource()) { + try (Jedis tmpRsc = plugin.requestJedis()) { return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found"); } } @@ -95,22 +87,22 @@ public class DataManager implements Listener { } catch (ExecutionException | UncheckedExecutionException e) { if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) return null; // HACK - plugin.getLogger().log(Level.SEVERE, "Unable to get proxy", e); + plugin.logFatal("Unable to get proxy"); throw new RuntimeException("Unable to get proxy for " + uuid, e); } } public InetAddress getIp(final UUID uuid) { - ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); + P player = plugin.getPlayer(uuid); if (player != null) - return player.getAddress().getAddress(); + return plugin.getPlayerIp(player); try { return ipCache.get(uuid, new Callable() { @Override public InetAddress call() throws Exception { - try (Jedis tmpRsc = plugin.getPool().getResource()) { + try (Jedis tmpRsc = plugin.requestJedis()) { String result = tmpRsc.hget("player:" + uuid, "ip"); if (result == null) throw new NullPointerException("user not found"); @@ -121,13 +113,13 @@ public class DataManager implements Listener { } catch (ExecutionException | UncheckedExecutionException e) { if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) return null; // HACK - plugin.getLogger().log(Level.SEVERE, "Unable to get IP", e); + plugin.logFatal( "Unable to get IP"); throw new RuntimeException("Unable to get IP for " + uuid, e); } } public long getLastOnline(final UUID uuid) { - ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); + P player = plugin.getPlayer(uuid); if (player != null) return 0; @@ -136,14 +128,14 @@ public class DataManager implements Listener { return lastOnlineCache.get(uuid, new Callable() { @Override public Long call() throws Exception { - try (Jedis tmpRsc = plugin.getPool().getResource()) { + try (Jedis tmpRsc = plugin.requestJedis()) { String result = tmpRsc.hget("player:" + uuid, "online"); return result == null ? -1 : Long.valueOf(result); } } }); } catch (ExecutionException e) { - plugin.getLogger().log(Level.SEVERE, "Unable to get last time online", e); + plugin.logFatal("Unable to get last time online"); throw new RuntimeException("Unable to get last time online for " + uuid, e); } } @@ -155,104 +147,153 @@ public class DataManager implements Listener { proxyCache.invalidate(uuid); } - @EventHandler - public void onPostLogin(PostLoginEvent event) { - // Invalidate all entries related to this player, since they now lie. - invalidate(event.getPlayer().getUniqueId()); + + public void onPostLogin(PL event) { + } - @EventHandler - public void onPlayerDisconnect(PlayerDisconnectEvent event) { - // Invalidate all entries related to this player, since they now lie. - invalidate(event.getPlayer().getUniqueId()); + public void onPlayerDisconnect(PD event) { + } - @EventHandler - public void onPubSubMessage(PubSubMessageEvent event) { - if (!event.getChannel().equals("redisbungee-data")) + public abstract void onPubSubMessage(PS event); + + protected void handlePubSubMessage(String channel, String message) { + if (!channel.equals("redisbungee-data")) return; // Partially deserialize the message so we can look at the action - JsonObject jsonObject = parser.parse(event.getMessage()).getAsJsonObject(); + JsonObject jsonObject = parser.parse(message).getAsJsonObject(); String source = jsonObject.get("source").getAsString(); - if (source.equals(RedisBungee.getConfiguration().getServerId())) + if (source.equals(plugin.getConfiguration().getServerId())) return; DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString()); switch (action) { case JOIN: - final DataManagerMessage message1 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() { + final DataManagerMessage message1 = gson.fromJson(jsonObject, new TypeToken() { }.getType()); proxyCache.put(message1.getTarget(), message1.getSource()); lastOnlineCache.put(message1.getTarget(), (long) 0); - ipCache.put(message1.getTarget(), message1.getPayload().getAddress()); - plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + ipCache.put(message1.getTarget(), ((LoginPayload)message1.getPayload()).getAddress()); + plugin.executeAsync(new Runnable() { @Override public void run() { - plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget())); + //plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget())); } }); break; case LEAVE: - final DataManagerMessage message2 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() { + final DataManagerMessage message2 = gson.fromJson(jsonObject, new TypeToken() { }.getType()); invalidate(message2.getTarget()); - lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp()); - plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + lastOnlineCache.put(message2.getTarget(), ((LogoutPayload)message2.getPayload()).getTimestamp()); + plugin.executeAsync(new Runnable() { @Override public void run() { - plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget())); + // plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget())); } }); break; case SERVER_CHANGE: - final DataManagerMessage message3 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() { + final DataManagerMessage message3 = gson.fromJson(jsonObject, new TypeToken() { }.getType()); - serverCache.put(message3.getTarget(), message3.getPayload().getServer()); - plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + serverCache.put(message3.getTarget(), ((ServerChangePayload)message3.getPayload()).getServer()); + plugin.executeAsync(new Runnable() { @Override public void run() { - plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getOldServer(), message3.getPayload().getServer())); + //plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getOldServer(), message3.getPayload().getServer())); } }); break; } } - @Getter - @RequiredArgsConstructor - static class DataManagerMessage { - private final UUID target; - private final String source = RedisBungee.getApi().getServerId(); - private final Action action; // for future use! - private final T payload; - enum Action { + + public static class DataManagerMessage { + private final UUID target; + private final String source; + private final Action action; // for future use! + private final Payload payload; + + public DataManagerMessage(UUID target, String source, Action action, Payload payload) { + this.target = target; + this.source = source; + this.action = action; + this.payload = payload; + } + + public UUID getTarget() { + return target; + } + + public String getSource() { + return source; + } + + public Action getAction() { + return action; + } + + public Payload getPayload() { + return payload; + } + + public enum Action { JOIN, LEAVE, SERVER_CHANGE } } - @Getter - @RequiredArgsConstructor - static class LoginPayload { - private final InetAddress address; + public static abstract class Payload { } - @Getter - @RequiredArgsConstructor - static class ServerChangePayload { + + public static class LoginPayload extends Payload{ + private final InetAddress address; + + public LoginPayload(InetAddress address) { + this.address = address; + } + + public InetAddress getAddress() { + return address; + } + } + + public static class ServerChangePayload extends Payload{ private final String server; private final String oldServer; + + ServerChangePayload(String server, String oldServer) { + this.server = server; + this.oldServer = oldServer; + } + + public String getServer() { + return server; + } + + public String getOldServer() { + return oldServer; + } } - @Getter - @RequiredArgsConstructor - static class LogoutPayload { + + public static class LogoutPayload extends Payload { private final long timestamp; + + public LogoutPayload(long timestamp) { + this.timestamp = timestamp; + } + + public long getTimestamp() { + return timestamp; + } } -} +} \ No newline at end of file diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/JedisPubSubHandler.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/JedisPubSubHandler.java new file mode 100644 index 0000000..453ab53 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/JedisPubSubHandler.java @@ -0,0 +1,47 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + +import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; +import redis.clients.jedis.JedisPubSub; + +import java.lang.reflect.InvocationTargetException; + +public class JedisPubSubHandler extends JedisPubSub { + + private final RedisBungeePlugin plugin; + + public JedisPubSubHandler(RedisBungeePlugin plugin) { + this.plugin = plugin; + } + + private Class bungeeEvent; + + @Override + public void onMessage(final String s, final String s2) { + if (s2.trim().length() == 0) return; + plugin.executeAsync(new Runnable() { + @Override + public void run() { + if (isBungeeEvent()) { + try { + Object object = bungeeEvent.getConstructor(String.class, String.class).newInstance(s, s2); + plugin.callEvent(object); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException("unable to fire pubsub event."); + } + return; + } + PubSubMessageEvent event = new PubSubMessageEvent(s, s2); + plugin.callEvent(event); + } + }); + } + + public boolean isBungeeEvent() { + return bungeeEvent != null; + } + + public void setBungeeEvent(Class clazz) { + bungeeEvent = clazz; + } +} \ No newline at end of file diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java new file mode 100644 index 0000000..c14a271 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java @@ -0,0 +1,71 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.exceptions.JedisConnectionException; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +public class PubSubListener implements Runnable { + private JedisPubSubHandler jpsh; + private Set addedChannels = new HashSet(); + + private final RedisBungeePlugin plugin; + + public PubSubListener(RedisBungeePlugin plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + boolean broken = false; + try (Jedis rsc = plugin.requestJedis()) { + try { + jpsh = new JedisPubSubHandler(plugin); + addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId()); + addedChannels.add("redisbungee-allservers"); + addedChannels.add("redisbungee-data"); + rsc.subscribe(jpsh, addedChannels.toArray(new String[0])); + } catch (Exception e) { + // FIXME: Extremely ugly hack + // Attempt to unsubscribe this instance and try again. + plugin.logWarn("PubSub error, attempting to recover."); + try { + jpsh.unsubscribe(); + } catch (Exception e1) { + /* This may fail with + - java.net.SocketException: Broken pipe + - redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance + */ + } + broken = true; + } + } catch (JedisConnectionException e) { + plugin.logWarn("PubSub error, attempting to recover in 5 secs."); + plugin.executeAsyncAfter(this, TimeUnit.SECONDS, 5); + } + + if (broken) { + run(); + } + } + + public void addChannel(String... channel) { + addedChannels.addAll(Arrays.asList(channel)); + jpsh.subscribe(channel); + } + + public void removeChannel(String... channel) { + Arrays.asList(channel).forEach(addedChannels::remove); + jpsh.unsubscribe(channel); + } + + public void poison() { + addedChannels.clear(); + jpsh.unsubscribe(); + } +} + diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeeConfiguration.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeeConfiguration.java new file mode 100644 index 0000000..55ec281 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeeConfiguration.java @@ -0,0 +1,36 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + +import com.google.common.collect.ImmutableList; +import com.google.common.net.InetAddresses; + +import java.net.InetAddress; +import java.util.List; + +public class RedisBungeeConfiguration { + private final String serverId; + private final List exemptAddresses; + private static RedisBungeeConfiguration config; + + public RedisBungeeConfiguration(String serverId, List exemptAddresses) { + this.serverId = serverId; + + ImmutableList.Builder addressBuilder = ImmutableList.builder(); + for (String s : exemptAddresses) { + addressBuilder.add(InetAddresses.forString(s)); + } + this.exemptAddresses = addressBuilder.build(); + config = this; + } + + public String getServerId() { + return serverId; + } + + public List getExemptAddresses() { + return exemptAddresses; + } + + public static RedisBungeeConfiguration getConfig() { + return config; + } +} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java new file mode 100644 index 0000000..3111728 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java @@ -0,0 +1,79 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + +import com.google.common.collect.Multimap; +import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI; +import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator; +import redis.clients.jedis.Jedis; + +import java.net.InetAddress; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public interface RedisBungeePlugin

{ + + void enable(); + + void disable(); + + RedisBungeeConfiguration getConfiguration(); + + int getCount(); + + DataManager getDataManager(); + + Set getPlayers(); + + Jedis requestJedis(); + + RedisBungeeAPI getApi(); + + UUIDTranslator getUuidTranslator(); + + Multimap serversToPlayers(); + + Set getPlayersOnProxy(String proxyId); + + void sendProxyCommand(String serverId, String command); + + List getServerIds(); + + PubSubListener getPubSubListener(); + + void sendChannelMessage(String channel, String message); + + void executeAsync(Runnable runnable); + + void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int seconds); + + void callEvent(Object object); + + boolean isOnlineMode(); + + void logInfo(String msg); + + void logWarn(String msg); + + void logFatal(String msg); + + boolean isPlayerServerNull(P player); + + P getPlayer(UUID uuid); + + P getPlayer(String name); + + UUID getPlayerUUID(String player); + + String getPlayerName(UUID player); + + String getPlayerServerName(P player); + + boolean isPlayerOnAServer(P player); + + InetAddress getPlayerIp(P player); + + void executeProxyCommand(String cmd); + + +} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisUtil.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisUtil.java new file mode 100644 index 0000000..697a1e6 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisUtil.java @@ -0,0 +1,51 @@ +package com.imaginarycode.minecraft.redisbungee.internal; + +import com.google.common.annotations.VisibleForTesting; +import com.google.gson.Gson; +import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Pipeline; + +import java.util.UUID; + +@VisibleForTesting +public class RedisUtil { + private static final Gson gson = new Gson(); + + public static void cleanUpPlayer(String player, Jedis rsc) { + rsc.srem("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", player); + rsc.hdel("player:" + player, "server", "ip", "proxy"); + long timestamp = System.currentTimeMillis(); + rsc.hset("player:" + player, "online", String.valueOf(timestamp)); + rsc.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage( + UUID.fromString(player), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.LEAVE, + new DataManager.LogoutPayload(timestamp)))); + } + + public static void cleanUpPlayer(String player, Pipeline rsc) { + rsc.srem("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", player); + rsc.hdel("player:" + player, "server", "ip", "proxy"); + long timestamp = System.currentTimeMillis(); + rsc.hset("player:" + player, "online", String.valueOf(timestamp)); + rsc.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage( + UUID.fromString(player), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.LEAVE, + new DataManager.LogoutPayload(timestamp)))); + } + + public static boolean isRedisVersionRight(String redisVersion) { + // Need to use >=6.2 to use Lua optimizations. + String[] args = redisVersion.split("\\."); + if (args.length < 2) { + return false; + } + int major = Integer.parseInt(args[0]); + int minor = Integer.parseInt(args[1]); + return major >= 6 && minor >= 0; + } + + // Ham1255: i am keeping this if some plugin uses this *IF* + @Deprecated + public static boolean canUseLua(String redisVersion) { + return isRedisVersionRight(redisVersion); + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/IOUtil.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/IOUtil.java similarity index 74% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/IOUtil.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/IOUtil.java index be44849..f2b943d 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/IOUtil.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/IOUtil.java @@ -1,14 +1,12 @@ -package com.imaginarycode.minecraft.redisbungee.util; +package com.imaginarycode.minecraft.redisbungee.internal.util; import com.google.common.io.ByteStreams; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -@NoArgsConstructor(access = AccessLevel.PRIVATE) + public class IOUtil { public static String readInputStreamAsString(InputStream is) { String string; diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/LuaManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java similarity index 58% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/LuaManager.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java index 42bcb91..008b2c2 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/LuaManager.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java @@ -1,32 +1,46 @@ -package com.imaginarycode.minecraft.redisbungee.util; +package com.imaginarycode.minecraft.redisbungee.internal.util; -import com.imaginarycode.minecraft.redisbungee.RedisBungee; -import lombok.RequiredArgsConstructor; +import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisDataException; import java.util.List; -@RequiredArgsConstructor public class LuaManager { - private final RedisBungee plugin; + private final RedisBungeePlugin plugin; + + public LuaManager(RedisBungeePlugin plugin) { + this.plugin = plugin; + } public Script createScript(String script) { - try (Jedis jedis = plugin.getPool().getResource()) { + try (Jedis jedis = plugin.requestJedis()) { String hash = jedis.scriptLoad(script); return new Script(script, hash); } } - @RequiredArgsConstructor public class Script { private final String script; private final String hashed; + public Script(String script, String hashed) { + this.script = script; + this.hashed = hashed; + } + + public String getScript() { + return script; + } + + public String getHashed() { + return hashed; + } + public Object eval(List keys, List args) { Object data; - try (Jedis jedis = plugin.getPool().getResource()) { + try (Jedis jedis = plugin.requestJedis()) { try { data = jedis.evalsha(hashed, keys, args); } catch (JedisDataException e) { diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/RedisCallable.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisCallable.java similarity index 69% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/RedisCallable.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisCallable.java index 11b3e49..3aa3e28 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/RedisCallable.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisCallable.java @@ -1,16 +1,19 @@ -package com.imaginarycode.minecraft.redisbungee.util; +package com.imaginarycode.minecraft.redisbungee.internal.util; -import com.imaginarycode.minecraft.redisbungee.RedisBungee; -import lombok.AllArgsConstructor; +import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisConnectionException; import java.util.concurrent.Callable; -import java.util.logging.Level; -@AllArgsConstructor + + public abstract class RedisCallable implements Callable, Runnable { - private final RedisBungee plugin; + private final RedisBungeePlugin plugin; + + public RedisCallable(RedisBungeePlugin plugin) { + this.plugin = plugin; + } @Override public T call() { @@ -22,10 +25,10 @@ public abstract class RedisCallable implements Callable, Runnable { } private T run(boolean retry) { - try (Jedis jedis = plugin.getPool().getResource()) { + try (Jedis jedis = plugin.requestJedis()) { return call(jedis); } catch (JedisConnectionException e) { - plugin.getLogger().log(Level.SEVERE, "Unable to get connection", e); + plugin.logFatal("Unable to get connection"); if (!retry) { // Wait one second before retrying the task diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/NameFetcher.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/NameFetcher.java similarity index 76% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/NameFetcher.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/NameFetcher.java index b7c035f..58c369a 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/NameFetcher.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/NameFetcher.java @@ -1,24 +1,23 @@ -package com.imaginarycode.minecraft.redisbungee.util.uuid; +package com.imaginarycode.minecraft.redisbungee.internal.util.uuid; +import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.ResponseBody; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.Setter; - import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.UUID; -@NoArgsConstructor(access = AccessLevel.PRIVATE) public class NameFetcher { - @Setter private static OkHttpClient httpClient; + private static final Gson gson = new Gson(); + + public static void setHttpClient(OkHttpClient httpClient) { + NameFetcher.httpClient = httpClient; + } public static List nameHistoryFromUuid(UUID uuid) throws IOException { String url = "https://api.mojang.com/user/profiles/" + uuid.toString().replace("-", "") + "/names"; @@ -29,7 +28,7 @@ public class NameFetcher { Type listType = new TypeToken>() { }.getType(); - List names = RedisBungee.getGson().fromJson(response, listType); + List names = gson.fromJson(response, listType); List humanNames = new ArrayList<>(); for (Name name : names) { diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDFetcher.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDFetcher.java similarity index 82% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDFetcher.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDFetcher.java index 61dca92..c8696fa 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDFetcher.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDFetcher.java @@ -1,9 +1,8 @@ -package com.imaginarycode.minecraft.redisbungee.util.uuid; +package com.imaginarycode.minecraft.redisbungee.internal.util.uuid; import com.google.common.collect.ImmutableList; -import com.imaginarycode.minecraft.redisbungee.RedisBungee; +import com.google.gson.Gson; import com.squareup.okhttp.*; -import lombok.Setter; import java.util.HashMap; import java.util.List; @@ -18,8 +17,13 @@ public class UUIDFetcher implements Callable> { private static final MediaType JSON = MediaType.parse("application/json"); private final List names; private final boolean rateLimiting; + private static final Gson gson = new Gson(); + + + public static void setHttpClient(OkHttpClient httpClient) { + UUIDFetcher.httpClient = httpClient; + } - @Setter private static OkHttpClient httpClient; private UUIDFetcher(List names, boolean rateLimiting) { @@ -39,12 +43,12 @@ public class UUIDFetcher implements Callable> { Map uuidMap = new HashMap<>(); int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); for (int i = 0; i < requests; i++) { - String body = RedisBungee.getGson().toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); + String body = gson.toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build(); ResponseBody responseBody = httpClient.newCall(request).execute().body(); String response = responseBody.string(); responseBody.close(); - Profile[] array = RedisBungee.getGson().fromJson(response, Profile[].class); + Profile[] array = gson.fromJson(response, Profile[].class); for (Profile profile : array) { UUID uuid = UUIDFetcher.getUUID(profile.id); uuidMap.put(profile.name, uuid); diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDTranslator.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDTranslator.java similarity index 77% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDTranslator.java rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDTranslator.java index 0554dbf..f2c8ff0 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/util/uuid/UUIDTranslator.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/uuid/UUIDTranslator.java @@ -1,13 +1,12 @@ -package com.imaginarycode.minecraft.redisbungee.util.uuid; +package com.imaginarycode.minecraft.redisbungee.internal.util.uuid; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; -import com.imaginarycode.minecraft.redisbungee.RedisBungee; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import net.md_5.bungee.api.ProxyServer; +import com.google.gson.Gson; +import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; + +import org.checkerframework.checker.nullness.qual.NonNull; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import redis.clients.jedis.exceptions.JedisException; @@ -17,13 +16,17 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.regex.Pattern; -@RequiredArgsConstructor public final class UUIDTranslator { private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"); private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}"); - private final RedisBungee plugin; + private final RedisBungeePlugin plugin; private final Map nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4); private final Map uuidToNameMap = new ConcurrentHashMap<>(128, 0.5f, 4); + private static final Gson gson = new Gson(); + + public UUIDTranslator(RedisBungeePlugin plugin) { + this.plugin = plugin; + } private void addToMaps(String name, UUID uuid) { // This is why I like LocalDate... @@ -41,8 +44,8 @@ public final class UUIDTranslator { public final UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) { // If the player is online, give them their UUID. // Remember, local data > remote data. - if (ProxyServer.getInstance().getPlayer(player) != null) - return ProxyServer.getInstance().getPlayer(player).getUniqueId(); + if (plugin.getPlayer(player) != null) + return plugin.getPlayerUUID(player); // Check if it exists in the map CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase()); @@ -65,16 +68,16 @@ public final class UUIDTranslator { // If we are in offline mode, UUID generation is simple. // We don't even have to cache the UUID, since this is easy to recalculate. - if (!plugin.getProxy().getConfig().isOnlineMode()) { + if (!plugin.isOnlineMode()) { return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8)); } // Let's try Redis. - try (Jedis jedis = plugin.getPool().getResource()) { + try (Jedis jedis = plugin.requestJedis()) { String stored = jedis.hget("uuid-cache", player.toLowerCase()); if (stored != null) { // Found an entry value. Deserialize it. - CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class); + CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class); // Check for expiry: if (entry.expired()) { @@ -89,14 +92,14 @@ public final class UUIDTranslator { } // That didn't work. Let's ask Mojang. - if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode()) + if (!expensiveLookups || !plugin.isOnlineMode()) return null; Map uuidMap1; try { uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call(); } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID from Mojang for " + player, e); + plugin.logFatal("Unable to fetch UUID from Mojang for " + player); return null; } for (Map.Entry entry : uuidMap1.entrySet()) { @@ -106,7 +109,7 @@ public final class UUIDTranslator { } } } catch (JedisException e) { - plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID for " + player, e); + plugin.logFatal("Unable to fetch UUID for " + player); } return null; // Nope, game over! @@ -115,8 +118,8 @@ public final class UUIDTranslator { public final String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) { // If the player is online, give them their UUID. // Remember, local data > remote data. - if (ProxyServer.getInstance().getPlayer(player) != null) - return ProxyServer.getInstance().getPlayer(player).getName(); + if (plugin.getPlayer(player) != null) + return plugin.getPlayerName(player); // Check if it exists in the map CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player); @@ -128,11 +131,11 @@ public final class UUIDTranslator { } // Okay, it wasn't locally cached. Let's try Redis. - try (Jedis jedis = plugin.getPool().getResource()) { + try (Jedis jedis = plugin.requestJedis()) { String stored = jedis.hget("uuid-cache", player.toString()); if (stored != null) { // Found an entry value. Deserialize it. - CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class); + CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class); // Check for expiry: if (entry.expired()) { @@ -147,7 +150,7 @@ public final class UUIDTranslator { } } - if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode()) + if (!expensiveLookups || !plugin.isOnlineMode()) return null; // That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane. @@ -156,7 +159,7 @@ public final class UUIDTranslator { List nameHist = NameFetcher.nameHistoryFromUuid(player); name = Iterables.getLast(nameHist, null); } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Unable to fetch name from Mojang for " + player, e); + plugin.logFatal("Unable to fetch name from Mojang for " + player); return null; } @@ -167,30 +170,46 @@ public final class UUIDTranslator { return null; } catch (JedisException e) { - plugin.getLogger().log(Level.SEVERE, "Unable to fetch name for " + player, e); + plugin.logFatal("Unable to fetch name for " + player); return null; } } public final void persistInfo(String name, UUID uuid, Jedis jedis) { addToMaps(name, uuid); - String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid)); + String json = gson.toJson(uuidToNameMap.get(uuid)); jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json)); } public final void persistInfo(String name, UUID uuid, Pipeline jedis) { addToMaps(name, uuid); - String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid)); + String json = gson.toJson(uuidToNameMap.get(uuid)); jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json)); } - @RequiredArgsConstructor - @Getter - private class CachedUUIDEntry { + private static class CachedUUIDEntry { private final String name; private final UUID uuid; private final Calendar expiry; + public CachedUUIDEntry(String name, UUID uuid, Calendar expiry) { + this.name = name; + this.uuid = uuid; + this.expiry = expiry; + } + + public String getName() { + return name; + } + + public UUID getUuid() { + return uuid; + } + + public Calendar getExpiry() { + return expiry; + } + public boolean expired() { return Calendar.getInstance().after(expiry); } diff --git a/RedisBungee-Bungee/pom.xml b/RedisBungee-Bungee/pom.xml new file mode 100644 index 0000000..24d951f --- /dev/null +++ b/RedisBungee-Bungee/pom.xml @@ -0,0 +1,110 @@ + + + + RedisBungee + com.imaginarycode.minecraft + 0.7.0-SNAPSHOT + + 4.0.0 + + RedisBungee-Bungee + + + 8 + 8 + + + + bungeecord-repo + https://oss.sonatype.org/content/repositories/snapshots + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + redis.clients.jedis + com.imaginarycode.minecraft.redisbungee.internal.jedis + + + + redis.clients.util + com.imaginarycode.minecraft.redisbungee.internal.jedisutil + + + + org.apache.commons.pool + com.imaginarycode.minecraft.redisbungee.internal.commonspool + + + + com.squareup.okhttp + com.imaginarycode.minecraft.redisbungee.internal.okhttp + + + + okio + com.imaginarycode.minecraft.redisbungee.internal.okio + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.0 + + 8 + + + + + + + + com.imaginarycode.minecraft + RedisBungee-API + 0.7.0-SNAPSHOT + + + net.md-5 + bungeecord-api + 1.17-R0.1-SNAPSHOT + jar + provided + + + + \ No newline at end of file diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RBUtils.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RBUtils.java new file mode 100644 index 0000000..66c0022 --- /dev/null +++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RBUtils.java @@ -0,0 +1,39 @@ +package com.imaginarycode.minecraft.redisbungee; + +import com.google.gson.Gson; +import com.imaginarycode.minecraft.redisbungee.internal.DataManager; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import redis.clients.jedis.Pipeline; + +import java.util.HashMap; +import java.util.Map; + +public class RBUtils { + + private static final Gson gson = new Gson(); + + protected static void createPlayer(ProxiedPlayer player, Pipeline pipeline, boolean fireEvent) { + createPlayer(player.getPendingConnection(), pipeline, fireEvent); + if (player.getServer() != null) + pipeline.hset("player:" + player.getUniqueId().toString(), "server", player.getServer().getInfo().getName()); + } + + protected static void createPlayer(PendingConnection connection, Pipeline pipeline, boolean fireEvent) { + Map playerData = new HashMap<>(4); + playerData.put("online", "0"); + playerData.put("ip", connection.getAddress().getAddress().getHostAddress()); + playerData.put("proxy", RedisBungeeAPI.getRedisBungeeApi().getServerId()); + + pipeline.sadd("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", connection.getUniqueId().toString()); + pipeline.hmset("player:" + connection.getUniqueId().toString(), playerData); + + if (fireEvent) { + pipeline.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage( + connection.getUniqueId(), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.JOIN, + new DataManager.LoginPayload(connection.getAddress().getAddress())))); + } + } + + +} diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java new file mode 100644 index 0000000..ac17a19 --- /dev/null +++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java @@ -0,0 +1,135 @@ +package com.imaginarycode.minecraft.redisbungee; + +import com.imaginarycode.minecraft.redisbungee.internal.AbstractRedisBungeeListener; +import com.imaginarycode.minecraft.redisbungee.internal.DataManager; +import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; +import com.imaginarycode.minecraft.redisbungee.events.bungee.PubSubMessageEvent; +import com.imaginarycode.minecraft.redisbungee.internal.RedisUtil; +import com.imaginarycode.minecraft.redisbungee.internal.util.RedisCallable; +import net.md_5.bungee.api.connection.ProxiedPlayer; +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.event.EventHandler; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Pipeline; + +import java.net.InetAddress; +import java.util.List; + +public class RedisBungeeListener extends AbstractRedisBungeeListener implements Listener { + + + public RedisBungeeListener(RedisBungeePlugin plugin, List exemptAddresses) { + super(plugin, exemptAddresses); + } + + @Override + @EventHandler + public void onLogin(LoginEvent event) { + event.registerIntent((Plugin) plugin); + plugin.executeAsync(new RedisCallable(plugin) { + @Override + protected Void call(Jedis jedis) { + try { + if (event.isCancelled()) { + return null; + } + + // We make sure they aren't trying to use an existing player's name. + // This is problematic for online-mode servers as they always disconnect old clients. + if (plugin.isOnlineMode()) { + ProxiedPlayer player = (ProxiedPlayer) plugin.getPlayer(event.getConnection().getName()); + + if (player != null) { + event.setCancelled(true); + // TODO: Make it accept a BaseComponent[] like everything else. + event.setCancelReason(ONLINE_MODE_RECONNECT); + return null; + } + } + + for (String s : plugin.getServerIds()) { + if (jedis.sismember("proxy:" + s + ":usersOnline", event.getConnection().getUniqueId().toString())) { + event.setCancelled(true); + // TODO: Make it accept a BaseComponent[] like everything else. + event.setCancelReason(ALREADY_LOGGED_IN); + return null; + } + } + return null; + } finally { + event.completeIntent((Plugin) plugin); + } + } + }); + } + + @Override + @EventHandler + public void onPostLogin(PostLoginEvent event) { + plugin.executeAsync(new RedisCallable(plugin) { + @Override + protected Void call(Jedis jedis) { + // this code was moved out from login event due being async.. + // and it can be cancelled but it will show as false in redis-bungee + // which will register the player into the redis database. + Pipeline pipeline = jedis.pipelined(); + plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), pipeline); + RBUtils.createPlayer(event.getPlayer(), pipeline, false); + pipeline.sync(); + // the end of moved code. + + jedis.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage( + event.getPlayer().getUniqueId(), plugin.getApi().getServerId(), DataManager.DataManagerMessage.Action.JOIN, + new DataManager.LoginPayload(event.getPlayer().getAddress().getAddress())))); + return null; + } + }); + } + + @Override + @EventHandler + public void onPlayerDisconnect(PlayerDisconnectEvent event) { + plugin.executeAsync(new RedisCallable(plugin) { + @Override + protected Void call(Jedis jedis) { + Pipeline pipeline = jedis.pipelined(); + RedisUtil.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), pipeline); + pipeline.sync(); + return null; + } + }); + + } + + @Override + @EventHandler + public void onServerChange(ServerConnectedEvent event) { + + } + + @Override + @EventHandler + public void onPing(ProxyPingEvent event) { + + } + + @Override + @EventHandler + public void onPluginMessage(PluginMessageEvent event) { + + } + + @Override + @EventHandler + public void onPubSubMessage(PubSubMessageEvent event) { + if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + plugin.getApi().getServerId())) { + String message = event.getMessage(); + if (message.startsWith("/")) + message = message.substring(1); + plugin.logInfo("Invoking command via PubSub: /" + message); + plugin.executeProxyCommand(message); + } + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/events/bungee/PubSubMessageEvent.java similarity index 69% rename from src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java rename to RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/events/bungee/PubSubMessageEvent.java index 967f31e..5eabc57 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java +++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/events/bungee/PubSubMessageEvent.java @@ -1,7 +1,5 @@ -package com.imaginarycode.minecraft.redisbungee.events; +package com.imaginarycode.minecraft.redisbungee.events.bungee; -import lombok.RequiredArgsConstructor; -import lombok.ToString; import net.md_5.bungee.api.plugin.Event; /** @@ -11,12 +9,16 @@ import net.md_5.bungee.api.plugin.Event; * * @since 0.2.6 */ -@RequiredArgsConstructor -@ToString + public class PubSubMessageEvent extends Event { private final String channel; private final String message; + public PubSubMessageEvent(String channel, String message) { + this.channel = channel; + this.message = message; + } + public String getChannel() { return channel; } diff --git a/pom.xml b/pom.xml index 91b3965..939f3e1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,87 +6,34 @@ com.imaginarycode.minecraft RedisBungee + pom 0.7.0-SNAPSHOT - - - - - bungeecord-repo - https://oss.sonatype.org/content/repositories/snapshots - - - - - - src/main/resources - true - - org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - - - redis.clients.jedis - com.imaginarycode.minecraft.redisbungee.internal.jedis - - - - redis.clients.util - com.imaginarycode.minecraft.redisbungee.internal.jedisutil - - - - org.apache.commons.pool - com.imaginarycode.minecraft.redisbungee.internal.commonspool - - - - com.squareup.okhttp - com.imaginarycode.minecraft.redisbungee.internal.okhttp - - - - okio - com.imaginarycode.minecraft.redisbungee.internal.okio - - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.3.0 8 + 8 + + RedisBungee-API + RedisBungee-Bungee + + + + + com.google.guava + guava + 31.1-jre + compile + redis.clients jedis @@ -99,19 +46,6 @@ 2.11.1 compile - - net.md-5 - bungeecord-api - 1.17-R0.1-SNAPSHOT - jar - provided - - - org.projectlombok - lombok - 1.18.22 - provided - com.squareup.okhttp okhttp diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java deleted file mode 100644 index 3594848..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java +++ /dev/null @@ -1,595 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.*; -import com.google.common.io.ByteStreams; -import com.google.gson.Gson; -import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; -import com.imaginarycode.minecraft.redisbungee.util.*; -import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher; -import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher; -import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDTranslator; -import com.squareup.okhttp.Dispatcher; -import com.squareup.okhttp.OkHttpClient; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; -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 redis.clients.jedis.*; -import redis.clients.jedis.exceptions.JedisConnectionException; - -import java.io.*; -import java.lang.reflect.Field; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * The RedisBungee plugin. - *

- * The only function of interest is {@link #getApi()}, which deprecated now, - * Please check {@link RedisBungeeAPI#getRedisBungeeApi()}, - * - * which exposes some functions in this class. - * but if you want old version support, - * then you can use old method {@link #getApi()} - * - */ -public final class RedisBungee extends Plugin { - @Getter - private static Gson gson = new Gson(); - private static RedisBungeeAPI api; - @Getter(AccessLevel.PACKAGE) - private static PubSubListener psl = null; - @Getter - private JedisPool pool; - @Getter - private UUIDTranslator uuidTranslator; - @Getter(AccessLevel.PACKAGE) - private static RedisBungeeConfiguration configuration; - @Getter - private DataManager dataManager; - @Getter - private static OkHttpClient httpClient; - private volatile List serverIds; - private final AtomicInteger nagAboutServers = new AtomicInteger(); - private final AtomicInteger globalPlayerCount = new AtomicInteger(); - private Future integrityCheck; - private Future heartbeatTask; - private LuaManager.Script serverToPlayersScript; - private LuaManager.Script getPlayerCountScript; - - private static final Object SERVER_TO_PLAYERS_KEY = new Object(); - private final Cache> serverToPlayersCache = CacheBuilder.newBuilder() - .expireAfterWrite(5, TimeUnit.SECONDS) - .build(); - - /** - * Fetch the {@link RedisBungeeAPI} object created on plugin start. - * - * @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} - * - * @return the {@link RedisBungeeAPI} object instance. - */ - @Deprecated - public static RedisBungeeAPI getApi() { - return api; - } - - static PubSubListener getPubSubListener() { - return psl; - } - - final List getServerIds() { - return serverIds; - } - - private List getCurrentServerIds(boolean nag, boolean lagged) { - try (Jedis jedis = pool.getResource()) { - long time = getRedisTime(jedis.time()); - int nagTime = 0; - if (nag) { - nagTime = nagAboutServers.decrementAndGet(); - if (nagTime <= 0) { - nagAboutServers.set(10); - } - } - ImmutableList.Builder servers = ImmutableList.builder(); - Map heartbeats = jedis.hgetAll("heartbeats"); - for (Map.Entry entry : heartbeats.entrySet()) { - try { - long stamp = Long.parseLong(entry.getValue()); - if (lagged ? time >= stamp + 30 : time <= stamp + 30) - servers.add(entry.getKey()); - else if (nag && nagTime <= 0) { - getLogger().warning(entry.getKey() + " is " + (time - stamp) + " seconds behind! (Time not synchronized or server down?) and was removed from heartbeat."); - jedis.hdel("heartbeats", entry.getKey()); - } - } catch (NumberFormatException ignored) { - } - } - return servers.build(); - } catch (JedisConnectionException e) { - getLogger().log(Level.SEVERE, "Unable to fetch server IDs", e); - return Collections.singletonList(configuration.getServerId()); - } - } - - public Set getPlayersOnProxy(String server) { - checkArgument(getServerIds().contains(server), server + " is not a valid proxy ID"); - try (Jedis jedis = pool.getResource()) { - Set users = jedis.smembers("proxy:" + server + ":usersOnline"); - ImmutableSet.Builder builder = ImmutableSet.builder(); - for (String user : users) { - builder.add(UUID.fromString(user)); - } - return builder.build(); - } - } - - final Multimap serversToPlayers() { - try { - return serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, new Callable>() { - @Override - public Multimap call() throws Exception { - Collection data = (Collection) serverToPlayersScript.eval(ImmutableList.of(), getServerIds()); - - ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); - String key = null; - for (String s : data) { - if (key == null) { - key = s; - continue; - } - - builder.put(key, UUID.fromString(s)); - key = null; - } - - return builder.build(); - } - }); - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - } - - final int getCount() { - return globalPlayerCount.get(); - } - - final int getCurrentCount() { - Long count = (Long) getPlayerCountScript.eval(ImmutableList.of(), ImmutableList.of()); - return count.intValue(); - } - - private Set getLocalPlayersAsUuidStrings() { - ImmutableSet.Builder builder = ImmutableSet.builder(); - for (ProxiedPlayer player : getProxy().getPlayers()) { - builder.add(player.getUniqueId().toString()); - } - return builder.build(); - } - - final Set getPlayers() { - ImmutableSet.Builder setBuilder = ImmutableSet.builder(); - if (pool != null) { - try (Jedis rsc = pool.getResource()) { - List keys = new ArrayList<>(); - for (String i : getServerIds()) { - keys.add("proxy:" + i + ":usersOnline"); - } - if (!keys.isEmpty()) { - Set users = rsc.sunion(keys.toArray(new String[keys.size()])); - if (users != null && !users.isEmpty()) { - for (String user : users) { - try { - setBuilder = setBuilder.add(UUID.fromString(user)); - } catch (IllegalArgumentException ignored) { - } - } - } - } - } catch (JedisConnectionException e) { - // Redis server has disappeared! - getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e); - throw new RuntimeException("Unable to get all players online", e); - } - } - return setBuilder.build(); - } - - final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) { - checkArgument(getServerIds().contains(proxyId) || proxyId.equals("allservers"), "proxyId is invalid"); - sendChannelMessage("redisbungee-" + proxyId, command); - } - - final void sendChannelMessage(String channel, String message) { - try (Jedis jedis = pool.getResource()) { - jedis.publish(channel, message); - } catch (JedisConnectionException e) { - // Redis server has disappeared! - getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e); - throw new RuntimeException("Unable to publish channel message", e); - } - } - - private long getRedisTime(List timeRes) { - return Long.parseLong(timeRes.get(0)); - } - - @Override - public void onEnable() { - ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory(); - ScheduledExecutorService service = Executors.newScheduledThreadPool(24, factory); - try { - Field field = Plugin.class.getDeclaredField("service"); - field.setAccessible(true); - ExecutorService builtinService = (ExecutorService) field.get(this); - field.set(this, service); - builtinService.shutdownNow(); - } catch (IllegalAccessException | NoSuchFieldException e) { - getLogger().log(Level.WARNING, "Can't replace BungeeCord thread pool with our own"); - getLogger().log(Level.INFO, "skipping replacement....."); - } - try { - loadConfig(); - } catch (IOException e) { - throw new RuntimeException("Unable to load/save config", e); - } catch (JedisConnectionException e) { - throw new RuntimeException("Unable to connect to your Redis server!", e); - } - if (pool != null) { - try (Jedis tmpRsc = pool.getResource()) { - // This is more portable than INFO

- String info = tmpRsc.info(); - for (String s : info.split("\r\n")) { - if (s.startsWith("redis_version:")) { - String version = s.split(":")[1]; - getLogger().info(version + " <- redis version"); - if (!RedisUtil.isRedisVersionRight(version)) { - getLogger().warning("Your version of Redis (" + version + ") is not at least version 6.0 RedisBungee requires a newer version of Redis."); - throw new RuntimeException("Unsupported Redis version detected"); - } else { - LuaManager manager = new LuaManager(this); - serverToPlayersScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/server_to_players.lua"))); - getPlayerCountScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/get_player_count.lua"))); - } - break; - } - } - - tmpRsc.hset("heartbeats", configuration.getServerId(), tmpRsc.time().get(0)); - - long uuidCacheSize = tmpRsc.hlen("uuid-cache"); - if (uuidCacheSize > 750000) { - getLogger().info("Looks like you have a really big UUID cache! Run https://www.spigotmc.org/resources/redisbungeecleaner.8505/ as soon as possible."); - } - } - serverIds = getCurrentServerIds(true, false); - uuidTranslator = new UUIDTranslator(this); - heartbeatTask = service.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try (Jedis rsc = pool.getResource()) { - long redisTime = getRedisTime(rsc.time()); - rsc.hset("heartbeats", configuration.getServerId(), String.valueOf(redisTime)); - } catch (JedisConnectionException e) { - // Redis server has disappeared! - getLogger().log(Level.SEVERE, "Unable to update heartbeat - did your Redis server go away?", e); - return; - } - try { - serverIds = getCurrentServerIds(true, false); - globalPlayerCount.set(getCurrentCount()); - } catch (Throwable e) { - getLogger().log(Level.SEVERE, "Unable to update data - did your Redis server go away?", e); - } - } - }, 0, 3, TimeUnit.SECONDS); - dataManager = new DataManager(this); - if (configuration.isRegisterBungeeCommands()) { - 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()); - getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlayerProxyCommand(this)); - getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlistCommand(this)); - getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.DebugCommand(this)); - api = new RedisBungeeAPI(this); - getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this, configuration.getExemptAddresses())); - getProxy().getPluginManager().registerListener(this, dataManager); - psl = new PubSubListener(); - getProxy().getScheduler().runAsync(this, psl); - integrityCheck = service.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try (Jedis tmpRsc = pool.getResource()) { - Set players = getLocalPlayersAsUuidStrings(); - Set playersInRedis = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline"); - List lagged = getCurrentServerIds(false, true); - - // Clean up lagged players. - for (String s : lagged) { - 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)..."); - for (String laggedPlayer : laggedPlayers) { - RedisUtil.cleanUpPlayer(laggedPlayer, tmpRsc); - } - } - } - - Set absentLocally = new HashSet<>(playersInRedis); - absentLocally.removeAll(players); - Set absentInRedis = new HashSet<>(players); - absentInRedis.removeAll(playersInRedis); - - for (String member : absentLocally) { - boolean found = false; - for (String proxyId : getServerIds()) { - if (proxyId.equals(configuration.getServerId())) continue; - if (tmpRsc.sismember("proxy:" + proxyId + ":usersOnline", member)) { - // Just clean up the set. - found = true; - break; - } - } - if (!found) { - RedisUtil.cleanUpPlayer(member, tmpRsc); - getLogger().warning("Player found in set that was not found locally and globally: " + member); - } else { - tmpRsc.srem("proxy:" + configuration.getServerId() + ":usersOnline", member); - getLogger().warning("Player found in set that was not found locally, but is on another proxy: " + member); - } - } - - Pipeline pipeline = tmpRsc.pipelined(); - - for (String player : absentInRedis) { - // Player not online according to Redis but not BungeeCord. - getLogger().warning("Player " + player + " is on the proxy but not in Redis."); - - ProxiedPlayer proxiedPlayer = ProxyServer.getInstance().getPlayer(UUID.fromString(player)); - if (proxiedPlayer == null) - continue; // We'll deal with it later. - - RedisUtil.createPlayer(proxiedPlayer, pipeline, true); - } - - pipeline.sync(); - } catch (Throwable e) { - getLogger().log(Level.SEVERE, "Unable to fix up stored player data", e); - } - } - }, 0, 1, TimeUnit.MINUTES); - } - getProxy().registerChannel("legacy:redisbungee"); - getProxy().registerChannel("RedisBungee"); - } - - @Override - public void onDisable() { - if (pool != null) { - // Poison the PubSub listener - psl.poison(); - integrityCheck.cancel(true); - heartbeatTask.cancel(true); - getProxy().getPluginManager().unregisterListeners(this); - - try (Jedis tmpRsc = pool.getResource()) { - tmpRsc.hdel("heartbeats", configuration.getServerId()); - if (tmpRsc.scard("proxy:" + configuration.getServerId() + ":usersOnline") > 0) { - Set players = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline"); - for (String member : players) - RedisUtil.cleanUpPlayer(member, tmpRsc); - } - } - - pool.destroy(); - } - } - - private void loadConfig() throws IOException, JedisConnectionException { - if (!getDataFolder().exists()) { - getDataFolder().mkdir(); - } - - File file = new File(getDataFolder(), "config.yml"); - - if (!file.exists()) { - file.createNewFile(); - try (InputStream in = getResourceAsStream("example_config.yml"); - OutputStream out = new FileOutputStream(file)) { - ByteStreams.copy(in, out); - } - } - - final Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); - - final String redisServer = configuration.getString("redis-server", "localhost"); - final int redisPort = configuration.getInt("redis-port", 6379); - final boolean useSSL = configuration.getBoolean("useSSL"); - String redisPassword = configuration.getString("redis-password"); - String serverId = configuration.getString("server-id"); - final String randomUUID = UUID.randomUUID().toString(); - - if (redisPassword != null && (redisPassword.isEmpty() || redisPassword.equals("none"))) { - redisPassword = null; - } - - // Configuration sanity checks. - if (serverId == null || serverId.isEmpty()) { - /* - * this check causes the config comments to disappear somehow - * 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."); - configuration.set("server-id", genId); - ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, new File(getDataFolder(), "config.yml")); - } else { - getLogger().info("Loaded server id " + serverId + '.'); - } - - if (configuration.getBoolean("use-random-id-string", false)) { - serverId = configuration.getString("server-id") + "-" + randomUUID; - } - - if (redisServer != null && !redisServer.isEmpty()) { - final String finalRedisPassword = redisPassword; - FutureTask task = new FutureTask<>(new Callable() { - @Override - public JedisPool call() throws Exception { - // Create the pool... - JedisPoolConfig config = new JedisPoolConfig(); - config.setMaxTotal(configuration.getInt("max-redis-connections", 8)); - return new JedisPool(config, redisServer, redisPort, 0, finalRedisPassword, useSSL); - } - }); - - getProxy().getScheduler().runAsync(this, task); - - try { - pool = task.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException("Unable to create Redis pool", e); - } - - // Test the connection - try (Jedis rsc = pool.getResource()) { - 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(); - } else if (rsc.hexists("heartbeats", serverId)) { - try { - long value = Long.parseLong(rsc.hget("heartbeats", serverId)); - long redisTime = getRedisTime(rsc.time()); - if (redisTime < value + 20) { - getLogger().severe("You have launched a possible impostor BungeeCord instance. Another instance is already running."); - getLogger().severe("For data consistency reasons, RedisBungee will now disable itself."); - getLogger().severe("If this instance is coming up from a crash, create a file in your RedisBungee plugins directory with the name 'restarted_from_crash.txt' and RedisBungee will not perform this check."); - throw new RuntimeException("Possible impostor instance!"); - } - } catch (NumberFormatException ignored) { - } - } - - FutureTask task2 = new FutureTask<>(new Callable() { - @Override - public Void call() throws Exception { - httpClient = new OkHttpClient(); - Dispatcher dispatcher = new Dispatcher(getExecutorService()); - httpClient.setDispatcher(dispatcher); - NameFetcher.setHttpClient(httpClient); - UUIDFetcher.setHttpClient(httpClient); - RedisBungee.configuration = new RedisBungeeConfiguration(RedisBungee.this.getPool(), configuration, randomUUID); - return null; - } - }); - - getProxy().getScheduler().runAsync(this, task2); - - try { - task2.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException("Unable to create HTTP client", e); - } - - getLogger().log(Level.INFO, "Successfully connected to Redis."); - } catch (JedisConnectionException e) { - pool.destroy(); - pool = null; - throw e; - } - } else { - throw new RuntimeException("No redis server specified!"); - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - class PubSubListener implements Runnable { - private JedisPubSubHandler jpsh; - - private Set addedChannels = new HashSet(); - - @Override - public void run() { - boolean broken = false; - try (Jedis rsc = pool.getResource()) { - try { - jpsh = new JedisPubSubHandler(); - addedChannels.add("redisbungee-" + configuration.getServerId()); - addedChannels.add("redisbungee-allservers"); - addedChannels.add("redisbungee-data"); - rsc.subscribe(jpsh, addedChannels.toArray(new String[0])); - } catch (Exception e) { - // FIXME: Extremely ugly hack - // Attempt to unsubscribe this instance and try again. - getLogger().log(Level.INFO, "PubSub error, attempting to recover.", e); - try { - jpsh.unsubscribe(); - } catch (Exception e1) { - /* This may fail with - - java.net.SocketException: Broken pipe - - redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance - */ - } - broken = true; - } - } catch (JedisConnectionException e) { - getLogger().log(Level.INFO, "PubSub error, attempting to recover in 5 secs."); - getProxy().getScheduler().schedule(RedisBungee.this, PubSubListener.this, 5, TimeUnit.SECONDS); - } - - if (broken) { - run(); - } - } - - public void addChannel(String... channel) { - addedChannels.addAll(Arrays.asList(channel)); - jpsh.subscribe(channel); - } - - public void removeChannel(String... channel) { - addedChannels.removeAll(Arrays.asList(channel)); - jpsh.unsubscribe(channel); - } - - public void poison() { - addedChannels.clear(); - jpsh.unsubscribe(); - } - } - - private class JedisPubSubHandler extends JedisPubSub { - @Override - public void onMessage(final String s, final String s2) { - if (s2.trim().length() == 0) return; - getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() { - @Override - public void run() { - getProxy().getPluginManager().callEvent(new PubSubMessageEvent(s, s2)); - } - }); - } - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java deleted file mode 100644 index c3e778e..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommandSender.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.BaseComponent; - -import java.util.Collection; -import java.util.Collections; - -/** - * 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 - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class RedisBungeeCommandSender implements CommandSender { - static final RedisBungeeCommandSender instance = new RedisBungeeCommandSender(); - - @Override - public String getName() { - return "RedisBungee"; - } - - @Override - public void sendMessage(String s) { - // no-op - } - - @Override - public void sendMessages(String... strings) { - // no-op - } - - @Override - public void sendMessage(BaseComponent... baseComponents) { - // no-op - } - - @Override - public void sendMessage(BaseComponent baseComponent) { - // no-op - } - - @Override - public Collection getGroups() { - return Collections.emptySet(); - } - - @Override - public void addGroups(String... strings) { - // no-op - } - - @Override - public void removeGroups(String... strings) { - // no-op - } - - @Override - public boolean hasPermission(String s) { - return true; - } - - @Override - public void setPermission(String s, boolean b) { - // no-op - } - - @Override - public Collection getPermissions() { - return Collections.emptySet(); - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommands.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommands.java deleted file mode 100644 index 3ccc507..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeCommands.java +++ /dev/null @@ -1,356 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import com.google.common.base.Joiner; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.plugin.Command; - -import java.net.InetAddress; -import java.text.SimpleDateFormat; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; - -/** - * This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen. - *

- * All classes use the {@link RedisBungeeAPI}. - * - * @author tuxed - * @since 0.2.3 - */ -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 String playerPlural(int num) { - return num == 1 ? num + " player is" : num + " players are"; - } - - public static class GlistCommand extends Command { - private final RedisBungee plugin; - - GlistCommand(RedisBungee 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 = RedisBungee.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 = RedisBungee.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 static class FindCommand extends Command { - private final RedisBungee plugin; - - FindCommand(RedisBungee 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 = RedisBungee.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 static class LastSeenCommand extends Command { - private final RedisBungee plugin; - - LastSeenCommand(RedisBungee 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 = RedisBungee.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 static class IpCommand extends Command { - private final RedisBungee plugin; - - IpCommand(RedisBungee 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 = RedisBungee.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 static class PlayerProxyCommand extends Command { - private final RedisBungee plugin; - - PlayerProxyCommand(RedisBungee 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 = RedisBungee.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 static class SendToAll extends Command { - private final RedisBungee plugin; - - SendToAll(RedisBungee plugin) { - super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall"); - this.plugin = plugin; - } - - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length > 0) { - String command = Joiner.on(" ").skipNulls().join(args); - RedisBungee.getApi().sendProxyCommand(command); - TextComponent message = new TextComponent(); - message.setColor(ChatColor.GREEN); - message.setText("Sent the command /" + command + " to all proxies."); - sender.sendMessage(message); - } else { - sender.sendMessage(NO_COMMAND_SPECIFIED); - } - } - } - - public static class ServerId extends Command { - private final RedisBungee plugin; - - ServerId(RedisBungee 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 " + RedisBungee.getApi().getServerId() + "."); - textComponent.setColor(ChatColor.YELLOW); - sender.sendMessage(textComponent); - } - } - - public static class ServerIds extends Command { - public ServerIds() { - super("serverids", "redisbungee.command.serverids"); - } - - @Override - public void execute(CommandSender sender, String[] strings) { - TextComponent textComponent = new TextComponent(); - textComponent.setText("All server IDs: " + Joiner.on(", ").join(RedisBungee.getApi().getAllServers())); - textComponent.setColor(ChatColor.YELLOW); - sender.sendMessage(textComponent); - } - } - - public static class PlistCommand extends Command { - private final RedisBungee plugin; - - PlistCommand(RedisBungee 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] : RedisBungee.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 = RedisBungee.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 = RedisBungee.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 static class DebugCommand extends Command { - private final RedisBungee plugin; - - DebugCommand(RedisBungee plugin) { - super("rdebug", "redisbungee.command.debug"); - this.plugin = plugin; - } - - @Override - public void execute(final CommandSender sender, final String[] args) { - TextComponent poolActiveStat = new TextComponent("Currently active pool objects: " + plugin.getPool().getNumActive()); - TextComponent poolIdleStat = new TextComponent("Currently idle pool objects: " + plugin.getPool().getNumIdle()); - TextComponent poolWaitingStat = new TextComponent("Waiting on free objects: " + plugin.getPool().getNumWaiters()); - sender.sendMessage(poolActiveStat); - sender.sendMessage(poolIdleStat); - sender.sendMessage(poolWaitingStat); - } - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConfiguration.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConfiguration.java deleted file mode 100644 index d5b68b1..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConfiguration.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import com.google.common.collect.ImmutableList; -import com.google.common.net.InetAddresses; -import lombok.Getter; -import net.md_5.bungee.config.Configuration; -import redis.clients.jedis.JedisPool; - -import java.net.InetAddress; -import java.util.List; -import java.util.UUID; - -public class RedisBungeeConfiguration { - @Getter - private final JedisPool pool; - @Getter - private final String serverId; - @Getter - private final boolean registerBungeeCommands; - @Getter - private final List exemptAddresses; - - - public RedisBungeeConfiguration(JedisPool pool, Configuration configuration, String randomUUID) { - this.pool = pool; - if (configuration.getBoolean("use-random-id-string", false)) { - this.serverId = configuration.getString("server-id") + "-" + randomUUID; - } else { - this.serverId = configuration.getString("server-id"); - } - - this.registerBungeeCommands = configuration.getBoolean("register-bungee-commands", true); - - List stringified = configuration.getStringList("exempt-ip-addresses"); - ImmutableList.Builder addressBuilder = ImmutableList.builder(); - - for (String s : stringified) { - addressBuilder.add(InetAddresses.forString(s)); - } - - this.exemptAddresses = addressBuilder.build(); - } - -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java deleted file mode 100644 index 04f5ffc..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java +++ /dev/null @@ -1,291 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import com.google.common.base.Joiner; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multiset; -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 com.imaginarycode.minecraft.redisbungee.util.RedisCallable; -import lombok.AllArgsConstructor; -import net.md_5.bungee.api.AbstractReconnectHandler; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ComponentBuilder; -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.event.EventHandler; -import net.md_5.bungee.event.EventPriority; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.Pipeline; - -import java.net.InetAddress; -import java.util.*; - -@AllArgsConstructor -public class RedisBungeeListener implements Listener { - private static final BaseComponent[] ALREADY_LOGGED_IN = - new ComponentBuilder("You are already logged on to this server.").color(ChatColor.RED) - .append("\n\nIt may help to try logging in again in a few minutes.\nIf this does not resolve your issue, please contact staff.") - .color(ChatColor.GRAY) - .create(); - private static final BaseComponent[] ONLINE_MODE_RECONNECT = - new ComponentBuilder("Whoops! You need to reconnect.").color(ChatColor.RED) - .append("\n\nWe found someone online using your username. They were kicked and you may reconnect.\nIf this does not work, please contact staff.") - .color(ChatColor.GRAY) - .create(); - private final RedisBungee plugin; - private final List exemptAddresses; - - @EventHandler(priority = EventPriority.LOWEST) - public void onLogin(final LoginEvent event) { - event.registerIntent(plugin); - plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable(plugin) { - @Override - protected Void call(Jedis jedis) { - try { - if (event.isCancelled()) { - return null; - } - - // We make sure they aren't trying to use an existing player's name. - // This is problematic for online-mode servers as they always disconnect old clients. - if (plugin.getProxy().getConfig().isOnlineMode()) { - ProxiedPlayer player = plugin.getProxy().getPlayer(event.getConnection().getName()); - - if (player != null) { - event.setCancelled(true); - // TODO: Make it accept a BaseComponent[] like everything else. - event.setCancelReason(ONLINE_MODE_RECONNECT); - return null; - } - } - - for (String s : plugin.getServerIds()) { - if (jedis.sismember("proxy:" + s + ":usersOnline", event.getConnection().getUniqueId().toString())) { - event.setCancelled(true); - // TODO: Make it accept a BaseComponent[] like everything else. - event.setCancelReason(ALREADY_LOGGED_IN); - return null; - } - } - return null; - } finally { - event.completeIntent(plugin); - } - } - }); - } - - @EventHandler - public void onPostLogin(final PostLoginEvent event) { - plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable(plugin) { - @Override - protected Void call(Jedis jedis) { - // this code was moved out from login event due being async.. - // and it can be cancelled but it will show as false in redis-bungee - // which will register the player into the redis database. - Pipeline pipeline = jedis.pipelined(); - plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), pipeline); - RedisUtil.createPlayer(event.getPlayer(), pipeline, false); - pipeline.sync(); - // the end of moved code. - - jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( - event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.JOIN, - new DataManager.LoginPayload(event.getPlayer().getAddress().getAddress())))); - return null; - } - }); - } - - @EventHandler - public void onPlayerDisconnect(final PlayerDisconnectEvent event) { - plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable(plugin) { - @Override - protected Void call(Jedis jedis) { - Pipeline pipeline = jedis.pipelined(); - RedisUtil.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), pipeline); - pipeline.sync(); - return null; - } - }); - } - - @EventHandler - public void onServerChange(final ServerConnectedEvent event) { - final String currentServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName(); - plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable(plugin) { - @Override - protected Void call(Jedis jedis) { - jedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName()); - jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( - event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE, - new DataManager.ServerChangePayload(event.getServer().getInfo().getName(), currentServer)))); - return null; - } - }); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onPing(final ProxyPingEvent event) { - if (exemptAddresses.contains(event.getConnection().getAddress().getAddress())) { - return; - } - - ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection()); - - if (forced != null && event.getConnection().getListener().isPingPassthrough()) { - return; - } - - event.getResponse().getPlayers().setOnline(plugin.getCount()); - } - - @SuppressWarnings("UnstableApiUsage") - @EventHandler - public void onPluginMessage(final PluginMessageEvent event) { - if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) { - final String currentChannel = event.getTag(); - final byte[] data = Arrays.copyOf(event.getData(), event.getData().length); - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - 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 = RedisBungee.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(RedisBungee.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(RedisBungee.getApi().getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(user, true))); - break; - case "ServerPlayers": - String type1 = in.readUTF(); - out.writeUTF("ServerPlayers"); - Multimap multimap = RedisBungee.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(RedisBungee.getConfiguration().getServerId()); - break; - case "PlayerProxy": - String username = in.readUTF(); - out.writeUTF("PlayerProxy"); - out.writeUTF(username); - out.writeUTF(RedisBungee.getApi().getProxy(plugin.getUuidTranslator().getTranslatedUuid(username, true))); - break; - default: - return; - } - - ((Server) event.getSender()).sendData(currentChannel, out.toByteArray()); - }); - } - } - - private void serializeMultiset(Multiset collection, ByteArrayDataOutput output) { - output.writeInt(collection.elementSet().size()); - for (Multiset.Entry entry : collection.entrySet()) { - output.writeUTF(entry.getElement()); - output.writeInt(entry.getCount()); - } - } - - @SuppressWarnings("SameParameterValue") - private void serializeMultimap(Multimap collection, boolean includeNames, ByteArrayDataOutput output) { - output.writeInt(collection.keySet().size()); - for (Map.Entry> entry : collection.asMap().entrySet()) { - output.writeUTF(entry.getKey()); - if (includeNames) { - serializeCollection(entry.getValue(), output); - } else { - output.writeInt(entry.getValue().size()); - } - } - } - - private void serializeCollection(Collection collection, ByteArrayDataOutput output) { - output.writeInt(collection.size()); - for (Object o : collection) { - output.writeUTF(o.toString()); - } - } - - @EventHandler - public void onPubSubMessage(PubSubMessageEvent event) { - if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + RedisBungee.getApi().getServerId())) { - 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 deleted file mode 100644 index c4fc059..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisUtil.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee; - -import com.google.common.annotations.VisibleForTesting; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.Pipeline; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@VisibleForTesting -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class RedisUtil { - protected static void createPlayer(ProxiedPlayer player, Pipeline pipeline, boolean fireEvent) { - createPlayer(player.getPendingConnection(), pipeline, fireEvent); - if (player.getServer() != null) - pipeline.hset("player:" + player.getUniqueId().toString(), "server", player.getServer().getInfo().getName()); - } - - protected static void createPlayer(PendingConnection connection, Pipeline pipeline, boolean fireEvent) { - Map playerData = new HashMap<>(4); - playerData.put("online", "0"); - playerData.put("ip", connection.getAddress().getAddress().getHostAddress()); - playerData.put("proxy", RedisBungee.getConfiguration().getServerId()); - - pipeline.sadd("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", connection.getUniqueId().toString()); - pipeline.hmset("player:" + connection.getUniqueId().toString(), playerData); - - if (fireEvent) { - pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( - connection.getUniqueId(), DataManager.DataManagerMessage.Action.JOIN, - new DataManager.LoginPayload(connection.getAddress().getAddress())))); - } - } - - public static void cleanUpPlayer(String player, Jedis rsc) { - rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player); - rsc.hdel("player:" + player, "server", "ip", "proxy"); - long timestamp = System.currentTimeMillis(); - rsc.hset("player:" + player, "online", String.valueOf(timestamp)); - rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( - UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE, - new DataManager.LogoutPayload(timestamp)))); - } - - public static void cleanUpPlayer(String player, Pipeline rsc) { - rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player); - rsc.hdel("player:" + player, "server", "ip", "proxy"); - long timestamp = System.currentTimeMillis(); - rsc.hset("player:" + player, "online", String.valueOf(timestamp)); - rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( - UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE, - new DataManager.LogoutPayload(timestamp)))); - } - - public static boolean isRedisVersionRight(String redisVersion) { - // Need to use >=6.2 to use Lua optimizations. - String[] args = redisVersion.split("\\."); - if (args.length < 2) { - return false; - } - int major = Integer.parseInt(args[0]); - int minor = Integer.parseInt(args[1]); - return major >= 6 && minor >= 0; - } - - // Ham1255: i am keeping this if some plugin uses this *IF* - @Deprecated - public static boolean canUseLua(String redisVersion) { - return isRedisVersionRight(redisVersion); - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java deleted file mode 100644 index 145b6d5..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.events; - -import lombok.ToString; -import net.md_5.bungee.api.plugin.Event; - -import java.util.UUID; - -/** - * This event is sent when a player connects to a new server. RedisBungee sends the event only when - * the proxy the player has been connected to is different than the local proxy. - *

- * This event corresponds to {@link net.md_5.bungee.api.event.ServerConnectedEvent}, and is fired - * asynchronously. - * - * @since 0.3.4 - */ -@ToString -public class PlayerChangedServerNetworkEvent extends Event { - private final UUID uuid; - private final String previousServer; - private final String server; - - public PlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server) { - this.uuid = uuid; - this.previousServer = previousServer; - this.server = server; - } - - public UUID getUuid() { - return uuid; - } - - public String getServer() { - return server; - } - - public String getPreviousServer() { - return previousServer; - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java deleted file mode 100644 index aeb700b..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.events; - -import lombok.ToString; -import net.md_5.bungee.api.plugin.Event; - -import java.util.UUID; - -/** - * This event is sent when a player joins the network. RedisBungee sends the event only when - * the proxy the player has been connected to is different than the local proxy. - *

- * This event corresponds to {@link net.md_5.bungee.api.event.PostLoginEvent}, and is fired - * asynchronously. - * - * @since 0.3.4 - */ -@ToString -public class PlayerJoinedNetworkEvent extends Event { - private final UUID uuid; - - public PlayerJoinedNetworkEvent(UUID uuid) { - this.uuid = uuid; - } - - public UUID getUuid() { - return uuid; - } -} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java deleted file mode 100644 index 23ded72..0000000 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.events; - -import lombok.ToString; -import net.md_5.bungee.api.plugin.Event; - -import java.util.UUID; - -/** - * This event is sent when a player disconnects. RedisBungee sends the event only when - * the proxy the player has been connected to is different than the local proxy. - *

- * This event corresponds to {@link net.md_5.bungee.api.event.PlayerDisconnectEvent}, and is fired - * asynchronously. - * - * @since 0.3.4 - */ -@ToString -public class PlayerLeftNetworkEvent extends Event { - private final UUID uuid; - - public PlayerLeftNetworkEvent(UUID uuid) { - this.uuid = uuid; - } - - public UUID getUuid() { - return uuid; - } -} diff --git a/src/main/resources/example_config.yml b/src/main/resources/example_config.yml deleted file mode 100644 index be0c1cd..0000000 --- a/src/main/resources/example_config.yml +++ /dev/null @@ -1,40 +0,0 @@ -# RedisBungee configuration file. -# PLEASE READ THE WIKI: https://github.com/Limework/RedisBungee/wiki - -# The Redis server you use. -# Get Redis from http://redis.io/ -redis-server: 127.0.0.1 -redis-port: 6379 -# OPTIONAL: If your Redis server uses AUTH, set the password required. -redis-password: "" -# Maximum connections that will be maintained to the Redis server. -# The default is 8. This setting should be left as-is unless you have some wildly -# inefficient plugins or a lot of players. -max-redis-connections: 8 - -# since redis can support ssl by version 6 you can use ssl in redis bungee too! -# you must disable this if redis version is under 6 you must disable this or connection wont work!!! -useSSL: false - -# An identifier for this BungeeCord instance. Will randomly generate if leaving it blank. -server-id: "test1" -# Should use random string? if this is enabled the proxy id will be like this if server-id is test1: "test1-66cd2aeb-91f3-43a7-a106-e0307b098652" -# or if id is limework-bungee it will be "limework-bungee-66cd2aeb-91f3-43a7-a106-e0307b098652" -# this great for servers who run replicas in Kubernetes or any auto deploying replica service -# and used for if proxy died in a kubernetes network and deleted then new proxy setup itself. -use-random-id-string: false - -# Whether or not RedisBungee should install its version of regular BungeeCord commands. -# Often, the RedisBungee commands are desired, but in some cases someone may wish to -# override the commands using another plugin. -# -# If you are just denying access to the commands, RedisBungee uses the default BungeeCord -# permissions - just deny them and access will be denied. -# -# Please note that with build 787+, most commands overridden by RedisBungee were moved to -# modules, and these must be disabled or overridden yourself. -register-bungee-commands: true - -# A list of IP addresses for which RedisBungee will not modify the response for, useful for automatic -# restart scripts. -exempt-ip-addresses: [] \ No newline at end of file diff --git a/src/main/resources/lua/get_player_count.lua b/src/main/resources/lua/get_player_count.lua deleted file mode 100644 index 0882aec..0000000 --- a/src/main/resources/lua/get_player_count.lua +++ /dev/null @@ -1,24 +0,0 @@ -local c = redis.call - -local curTime = c("TIME") -local time = tonumber(curTime[1]) - -local heartbeats = c("HGETALL", "heartbeats") -local total = 0 -local key - -for _, v in ipairs(heartbeats) do - if not key then - key = v - else - local n = tonumber(v) - if n then - if n + 30 >= time then - total = total + c("SCARD", "proxy:" .. key .. ":usersOnline") - end - end - key = nil - end -end - -return total diff --git a/src/main/resources/lua/server_to_players.lua b/src/main/resources/lua/server_to_players.lua deleted file mode 100644 index ee66398..0000000 --- a/src/main/resources/lua/server_to_players.lua +++ /dev/null @@ -1,18 +0,0 @@ -local call = redis.call -local ipairs = ipairs - -local serverToData = {} - -for _, proxy in ipairs(ARGV) do - local players = call("SMEMBERS", "proxy:" .. proxy .. ":usersOnline") - for _, player in ipairs(players) do - local server = call("HGET", "player:" .. player, "server") - if server then - local sz = #serverToData - serverToData[sz + 1] = server - serverToData[sz + 2] = player - end - end -end - -return serverToData \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml deleted file mode 100644 index 27b3ff5..0000000 --- a/src/main/resources/plugin.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: ${project.name} -main: com.imaginarycode.minecraft.redisbungee.RedisBungee -version: ${project.version} -author: Chunkr and Govindas limework -authors: - - chunkr - - Govindas Limework -# This is used so that we can automatically override default BungeeCord behavior. -softDepends: ["cmd_find", "cmd_list"] \ No newline at end of file diff --git a/src/test/java/com/imaginarycode/minecraft/redisbungee/test/RedisUtilTest.java b/src/test/java/com/imaginarycode/minecraft/redisbungee/test/RedisUtilTest.java deleted file mode 100644 index 5f03f88..0000000 --- a/src/test/java/com/imaginarycode/minecraft/redisbungee/test/RedisUtilTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.test; - -import com.imaginarycode.minecraft.redisbungee.RedisUtil; -import org.junit.Assert; -import org.junit.Test; - -public class RedisUtilTest { - @Test - public void testRedisLuaCheck() { - Assert.assertTrue(RedisUtil.canUseLua("6.2.0")); - Assert.assertTrue(RedisUtil.canUseLua("6.1.0")); - Assert.assertTrue(RedisUtil.canUseLua("6.0.0")); - Assert.assertFalse(RedisUtil.canUseLua("2.6.0")); - Assert.assertFalse(RedisUtil.canUseLua("2.2.12")); - Assert.assertFalse(RedisUtil.canUseLua("1.2.4")); - Assert.assertFalse(RedisUtil.canUseLua("2.8.4")); - Assert.assertFalse(RedisUtil.canUseLua("3.0.0")); - Assert.assertFalse(RedisUtil.canUseLua("3.2.1")); - } -} diff --git a/src/test/java/com/imaginarycode/minecraft/redisbungee/test/UUIDNameTest.java b/src/test/java/com/imaginarycode/minecraft/redisbungee/test/UUIDNameTest.java deleted file mode 100644 index 613f97b..0000000 --- a/src/test/java/com/imaginarycode/minecraft/redisbungee/test/UUIDNameTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.test; - -import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher; -import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher; -import com.squareup.okhttp.OkHttpClient; -import org.junit.Test; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -public class UUIDNameTest { - private String[] uuidsToTest = {"68ec43f7234b41b48764dfb38b9ffe8c", "652a2bc4e8cd405db7b698156ee2dc09"}; - private String[] namesToTest = {"vemacs"}; - - @Test - public void testUuidToName() throws IOException { - OkHttpClient httpClient = new OkHttpClient(); - NameFetcher.setHttpClient(httpClient); - for (String uuid : uuidsToTest) { - List names = NameFetcher.nameHistoryFromUuid(UUIDFetcher.getUUID(uuid)); - String currentName = names.get(names.size() - 1); - System.out.println("Current name for UUID " + uuid + " is " + currentName); - } - } - - @Test - public void testNameToUuid() throws IOException { - OkHttpClient httpClient = new OkHttpClient(); - UUIDFetcher.setHttpClient(httpClient); - for (String name : namesToTest) { - Map uuidMap1; - try { - uuidMap1 = new UUIDFetcher(Collections.singletonList(name)).call(); - for (Map.Entry entry : uuidMap1.entrySet()) { - if (entry.getKey().equalsIgnoreCase(name)) { - System.out.println("Current UUID for name " + name + " is " + entry.getValue()); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } -}