getPlayersOnProxy(@NonNull String proxyID) {
+ return plugin.proxyDataManager().getPlayersOn(proxyID);
}
/**
@@ -163,7 +153,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.2.4
*/
public final InetAddress getPlayerIp(@NonNull UUID player) {
- return plugin.getDataManager().getIp(player);
+ return plugin.playerDataManager().getIpFor(player);
}
/**
@@ -174,7 +164,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.3.3
*/
public final String getProxy(@NonNull UUID player) {
- return plugin.getDataManager().getProxy(player);
+ return plugin.playerDataManager().getProxyFor(player);
}
/**
@@ -185,7 +175,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.2.5
*/
public final void sendProxyCommand(@NonNull String command) {
- plugin.sendProxyCommand("allservers", command);
+ sendProxyCommand("allservers", command);
}
/**
@@ -198,19 +188,20 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.2.5
*/
public final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) {
- plugin.sendProxyCommand(proxyId, command);
+ plugin.proxyDataManager().sendCommandTo(proxyId, command);
}
/**
- * Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for
- * PubSubMessageEvent to fire.
+ * Sends a message to a PubSub channel which makes PubSubMessageEvent fire.
+ *
+ * Note: Since 0.12.0 registering a channel api is no longer required
*
* @param channel The PubSub channel
* @param message the message body to send
* @since 0.3.3
*/
public final void sendChannelMessage(@NonNull String channel, @NonNull String message) {
- plugin.sendChannelMessage(channel, message);
+ plugin.proxyDataManager().sendChannelMessage(channel, message);
}
/**
@@ -221,7 +212,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.8.0
*/
public final String getProxyId() {
- return plugin.getConfiguration().getProxyId();
+ return plugin.proxyDataManager().proxyId();
}
/**
@@ -245,7 +236,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.8.0
*/
public final List getAllProxies() {
- return plugin.getProxiesIds();
+ return plugin.proxyDataManager().proxiesIds();
}
/**
@@ -266,9 +257,10 @@ public abstract class AbstractRedisBungeeAPI {
*
* @param channels the channels to register
* @since 0.3
+ * @deprecated No longer required
*/
+ @Deprecated
public final void registerPubSubChannels(String... channels) {
- plugin.getPubSubListener().addChannel(channels);
}
/**
@@ -276,13 +268,10 @@ public abstract class AbstractRedisBungeeAPI {
*
* @param channels the channels to unregister
* @since 0.3
+ * @deprecated No longer required
*/
+ @Deprecated
public final void unregisterPubSubChannels(String... channels) {
- for (String channel : channels) {
- Preconditions.checkArgument(!reservedChannels.contains(channel), "attempting to unregister internal channel");
- }
-
- plugin.getPubSubListener().removeChannel(channels);
}
/**
@@ -355,6 +344,7 @@ public abstract class AbstractRedisBungeeAPI {
/**
* Kicks a player from the network
+ * calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @param message kick message that player will see on kick
@@ -362,7 +352,7 @@ public abstract class AbstractRedisBungeeAPI {
*/
public void kickPlayer(String playerName, String message) {
- plugin.kickPlayer(playerName, message);
+ kickPlayer(getUuidFromName(playerName), message);
}
/**
@@ -373,7 +363,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.8.0
*/
public void kickPlayer(UUID playerUUID, String message) {
- plugin.kickPlayer(playerUUID, message);
+ this.plugin.playerDataManager().kickPlayer(playerUUID, message);
}
/**
@@ -457,6 +447,7 @@ public abstract class AbstractRedisBungeeAPI {
/**
* shows what mode is RedisBungee is on
+ * Basically what every redis mode is used like cluster or single instance.
*
* @return {@link RedisBungeeMode}
* @since 0.8.0
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/AbstractDataManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/AbstractDataManager.java
deleted file mode 100644
index 1bff5b5..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/AbstractDataManager.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api;
-
-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.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.net.InetAddress;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class manages all the data that RedisBungee fetches from Redis, along with updates to that data.
- *
- * @since 0.3.3
- */
-public abstract class AbstractDataManager {
- protected 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 AbstractDataManager(RedisBungeePlugin plugin) {
- this.plugin = plugin;
- }
-
- private static Cache createCache() {
- // TODO: Allow customization via cache specification, ala ServerListPlus
- return CacheBuilder.newBuilder()
- .maximumSize(1000)
- .expireAfterWrite(1, TimeUnit.HOURS)
- .build();
- }
-
- public String getServer(final UUID uuid) {
- P player = plugin.getPlayer(uuid);
-
- if (player != null)
- return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null;
-
- try {
- return serverCache.get(uuid, new RedisTask(plugin) {
- @Override
- public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
- return Objects.requireNonNull(unifiedJedis.hget("player:" + uuid, "server"), "user not found");
-
- }
- });
- } catch (ExecutionException | UncheckedExecutionException e) {
- if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
- return null; // HACK
- plugin.logFatal("Unable to get server");
- throw new RuntimeException("Unable to get server for " + uuid, e);
- }
- }
-
-
- public String getProxy(final UUID uuid) {
- P player = plugin.getPlayer(uuid);
-
- if (player != null)
- return plugin.getConfiguration().getProxyId();
-
- try {
- return proxyCache.get(uuid, new RedisTask(plugin) {
- @Override
- public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
- return Objects.requireNonNull(unifiedJedis.hget("player:" + uuid, "proxy"), "user not found");
- }
- });
- } catch (ExecutionException | UncheckedExecutionException e) {
- if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
- return null; // HACK
- plugin.logFatal("Unable to get proxy");
- throw new RuntimeException("Unable to get proxy for " + uuid, e);
- }
- }
-
- public InetAddress getIp(final UUID uuid) {
- P player = plugin.getPlayer(uuid);
-
- if (player != null)
- return plugin.getPlayerIp(player);
-
- try {
- return ipCache.get(uuid, new RedisTask(plugin) {
- @Override
- public InetAddress unifiedJedisTask(UnifiedJedis unifiedJedis) {
- String result = unifiedJedis.hget("player:" + uuid, "ip");
- if (result == null)
- throw new NullPointerException("user not found");
- return InetAddresses.forString(result);
- }
- });
- } catch (ExecutionException | UncheckedExecutionException e) {
- if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
- return null; // HACK
- plugin.logFatal("Unable to get IP");
- throw new RuntimeException("Unable to get IP for " + uuid, e);
- }
- }
-
- public long getLastOnline(final UUID uuid) {
- P player = plugin.getPlayer(uuid);
-
- if (player != null)
- return 0;
-
- try {
- return lastOnlineCache.get(uuid, new RedisTask(plugin) {
-
- @Override
- public Long unifiedJedisTask(UnifiedJedis unifiedJedis) {
- String result = unifiedJedis.hget("player:" + uuid, "online");
- return result == null ? -1 : Long.parseLong(result);
- }
- });
- } catch (ExecutionException e) {
- plugin.logFatal("Unable to get last time online");
- throw new RuntimeException("Unable to get last time online for " + uuid, e);
- }
- }
-
- protected void invalidate(UUID uuid) {
- ipCache.invalidate(uuid);
- lastOnlineCache.invalidate(uuid);
- serverCache.invalidate(uuid);
- proxyCache.invalidate(uuid);
- }
-
- // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
- public abstract void onPostLogin(PL event);
-
- // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
- public abstract void onPlayerDisconnect(PD event);
-
- public abstract void onPubSubMessage(PS event);
-
- public abstract boolean handleKick(UUID target, String message);
-
- 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 = JsonParser.parseString(message).getAsJsonObject();
-
- final String source = jsonObject.get("source").getAsString();
-
- if (source.equals(plugin.getConfiguration().getProxyId()))
- return;
-
- DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString());
-
- switch (action) {
- case JOIN:
- 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.executeAsync(() -> {
- Object event = plugin.createPlayerJoinedNetworkEvent(message1.getTarget());
- plugin.fireEvent(event);
- });
- break;
- case LEAVE:
- final DataManagerMessage message2 = gson.fromJson(jsonObject, new TypeToken>() {
- }.getType());
- invalidate(message2.getTarget());
- lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp());
- plugin.executeAsync(() -> {
- Object event = plugin.createPlayerLeftNetworkEvent(message2.getTarget());
- plugin.fireEvent(event);
- });
- break;
- case SERVER_CHANGE:
- final DataManagerMessage message3 = gson.fromJson(jsonObject, new TypeToken>() {
- }.getType());
- serverCache.put(message3.getTarget(), message3.getPayload().getServer());
- plugin.executeAsync(() -> {
- Object event = plugin.createPlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getOldServer(), message3.getPayload().getServer());
- plugin.fireEvent(event);
- });
- break;
- case KICK:
- final DataManagerMessage kickPayload = gson.fromJson(jsonObject, new TypeToken>() {
- }.getType());
- plugin.executeAsync(() -> handleKick(kickPayload.target, kickPayload.payload.message));
- break;
-
- }
- }
-
- public static class DataManagerMessage {
- private final UUID target;
- private final String source;
- private final Action action; // for future use!
- private final T payload;
-
- public DataManagerMessage(UUID target, String source, Action action, T 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 T getPayload() {
- return payload;
- }
-
- public enum Action {
- JOIN,
- LEAVE,
- KICK,
- SERVER_CHANGE
- }
- }
-
- public static abstract class Payload {
- }
-
- public static class KickPayload extends Payload {
-
- private final String message;
-
- public KickPayload(String message) {
- this.message = message;
- }
-
- public String getMessage() {
- return message;
- }
- }
-
- 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;
-
- public ServerChangePayload(String server, String oldServer) {
- this.server = server;
- this.oldServer = oldServer;
- }
-
- public String getServer() {
- return server;
- }
-
- public String getOldServer() {
- return oldServer;
- }
- }
-
-
- 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/api/AbstractRedisBungeeListener.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/AbstractRedisBungeeListener.java
deleted file mode 100644
index 64b42ab..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/AbstractRedisBungeeListener.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api;
-
-
-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 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 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);
-
- public abstract void onPubSubMessage(PS event);
-
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/JedisPubSubHandler.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/JedisPubSubHandler.java
deleted file mode 100644
index d3974a5..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/JedisPubSubHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api;
-
-
-import redis.clients.jedis.JedisPubSub;
-
-
-public class JedisPubSubHandler extends JedisPubSub {
-
- private final RedisBungeePlugin> plugin;
-
- public JedisPubSubHandler(RedisBungeePlugin> plugin) {
- this.plugin = plugin;
- }
-
- @Override
- public void onMessage(final String s, final String s2) {
- if (s2.trim().length() == 0) return;
- plugin.executeAsync(new Runnable() {
- @Override
- public void run() {
- Object event = plugin.createPubSubEvent(s, s2);
- plugin.fireEvent(event);
- }
- });
- }
-}
\ No newline at end of file
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PlayerDataManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PlayerDataManager.java
new file mode 100644
index 0000000..c6ccbb6
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PlayerDataManager.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.net.InetAddresses;
+import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
+import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
+import org.json.JSONObject;
+import redis.clients.jedis.ClusterPipeline;
+import redis.clients.jedis.Pipeline;
+import redis.clients.jedis.Response;
+import redis.clients.jedis.UnifiedJedis;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+public abstract class PlayerDataManager {
+
+ protected final RedisBungeePlugin
plugin;
+ private final LoadingCache serverCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis);
+ private final LoadingCache proxyCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getProxyFromRedis);
+ private final LoadingCache ipCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getIpAddressFromRedis);
+ private final LoadingCache lastOnlineCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastOnlineFromRedis);
+ private final Object SERVERS_TO_PLAYERS_KEY = new Object();
+ private final LoadingCache> serverToPlayersCache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(this::serversToPlayersBuilder);
+ private final UnifiedJedis unifiedJedis;
+
+ public PlayerDataManager(RedisBungeePlugin plugin) {
+ this.plugin = plugin;
+ this.unifiedJedis = plugin.proxyDataManager().unifiedJedis();
+ }
+
+ // handle network wide
+ // server change
+ public abstract void onPlayerChangedServerNetworkEvent(SC event);
+
+ public abstract void onNetworkPlayerQuit(NJE event);
+
+ // local events
+ public abstract void onPubSubMessageEvent(PS event);
+
+ public abstract void onServerConnectedEvent(CE event);
+
+ public abstract void onLoginEvent(LE event);
+
+ public abstract void onDisconnectEvent(DE event);
+
+
+ protected void handleNetworkPlayerServerChange(IPlayerChangedServerNetworkEvent event) {
+ this.serverCache.invalidate(event.getUuid());
+ }
+
+ protected void handleNetworkPlayerQuit(IPlayerLeftNetworkEvent event) {
+ this.proxyCache.invalidate(event.getUuid());
+ this.serverCache.invalidate(event.getUuid());
+ this.ipCache.invalidate(event.getUuid());
+ this.lastOnlineCache.invalidate(event.getUuid());
+ }
+
+ protected void handlePubSubMessageEvent(IPubSubMessageEvent event) {
+ // kick api
+ if (event.getChannel().equals("redisbungee-kick")) {
+ JSONObject data = new JSONObject(event.getMessage());
+ String proxy = data.getString("proxy");
+ if (proxy.equals(plugin.configuration().getProxyId())) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ String message = data.getString("message");
+ plugin.handlePlatformKick(uuid, message);
+ return;
+ }
+ if (event.getChannel().equals("redisbungee-serverchange")) {
+ JSONObject data = new JSONObject(event.getMessage());
+ String proxy = data.getString("proxy");
+ if (proxy.equals(plugin.configuration().getProxyId())) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ String from = data.getString("from");
+ String to = data.getString("to");
+ plugin.fireEvent(plugin.createPlayerChangedServerNetworkEvent(uuid, from, to));
+ return;
+ }
+ if (event.getChannel().equals("redisbungee-player-join")) {
+ JSONObject data = new JSONObject(event.getMessage());
+ String proxy = data.getString("proxy");
+ if (proxy.equals(plugin.configuration().getProxyId())) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ plugin.fireEvent(plugin.createPlayerJoinedNetworkEvent(uuid));
+ return;
+ }
+ if (event.getChannel().equals("redisbungee-player-leave")) {
+ JSONObject data = new JSONObject(event.getMessage());
+ String proxy = data.getString("proxy");
+ if (proxy.equals(plugin.configuration().getProxyId())) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
+ }
+
+ }
+
+ protected void playerChangedServer(UUID uuid, String from, String to) {
+ JSONObject data = new JSONObject();
+ data.put("proxy", plugin.configuration().getProxyId());
+ data.put("uuid", uuid);
+ data.put("from", from);
+ data.put("to", to);
+ plugin.proxyDataManager().sendChannelMessage("redisbungee-serverchange", data.toString());
+ plugin.fireEvent(plugin.createPlayerChangedServerNetworkEvent(uuid, from, to));
+ handleServerChangeRedis(uuid, to);
+ }
+
+ public void kickPlayer(UUID uuid, String message) {
+ if (!plugin.handlePlatformKick(uuid, message)) { // handle locally before SENDING a message
+ JSONObject data = new JSONObject();
+ data.put("proxy", plugin.configuration().getProxyId());
+ data.put("uuid", uuid);
+ data.put("message", message);
+ plugin.proxyDataManager().sendChannelMessage("redisbungee-kick", data.toString());
+ }
+ }
+
+ private void handleServerChangeRedis(UUID uuid, String server) {
+ Map data = new HashMap<>();
+ data.put("server", server);
+ data.put("last-server", server);
+ unifiedJedis.hset("redis-bungee::player::" + uuid + "::data", data);
+ }
+
+ protected void addPlayer(final UUID uuid, final InetAddress inetAddress) {
+ Map redisData = new HashMap<>();
+ redisData.put("last-online", String.valueOf(0));
+ redisData.put("proxy", plugin.configuration().getProxyId());
+ redisData.put("ip", inetAddress.toString());
+ unifiedJedis.hset("redis-bungee::player::" + uuid + "::data", redisData);
+
+ JSONObject data = new JSONObject();
+ data.put("proxy", plugin.configuration().getProxyId());
+ data.put("uuid", uuid);
+ plugin.proxyDataManager().sendChannelMessage("redisbungee-player-join", data.toString());
+ plugin.fireEvent(plugin.createPlayerJoinedNetworkEvent(uuid));
+ this.plugin.proxyDataManager().addPlayer(uuid);
+ }
+
+ protected void removePlayer(UUID uuid) {
+ unifiedJedis.hset("redis-bungee::player::" + uuid + "::data", "last-online", String.valueOf(System.currentTimeMillis()));
+ unifiedJedis.hdel("redis-bungee::player::" + uuid + "::data", "server", "proxy", "ip");
+ JSONObject data = new JSONObject();
+ data.put("proxy", plugin.configuration().getProxyId());
+ data.put("uuid", uuid);
+ plugin.proxyDataManager().sendChannelMessage("redisbungee-player-leave", data.toString());
+ plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
+ this.plugin.proxyDataManager().removePlayer(uuid);
+ }
+
+
+ protected String getProxyFromRedis(UUID uuid) {
+ return unifiedJedis.hget("redis-bungee::player::" + uuid + "::data", "proxy");
+ }
+
+ protected String getServerFromRedis(UUID uuid) {
+ return unifiedJedis.hget("redis-bungee::player::" + uuid + "::data", "server");
+ }
+
+ protected InetAddress getIpAddressFromRedis(UUID uuid) {
+ String ip = unifiedJedis.hget("redis-bungee::player::" + uuid + "::data", "ip");
+ if (ip == null) return null;
+ return InetAddresses.forString(ip);
+ }
+
+ protected long getLastOnlineFromRedis(UUID uuid) {
+ String unixString = unifiedJedis.hget("redis-bungee::player::" + uuid + "::data", "last-online");
+ if (unixString == null) return -1;
+ return Long.parseLong(unixString);
+ }
+
+ public String getServerFor(UUID uuid) {
+ return this.serverCache.get(uuid);
+ }
+
+ public String getProxyFor(UUID uuid) {
+ return this.proxyCache.get(uuid);
+ }
+
+ public InetAddress getIpFor(UUID uuid) {
+ return this.ipCache.get(uuid);
+ }
+
+ public long getLastOnline(UUID uuid) {
+ return this.lastOnlineCache.get(uuid);
+ }
+
+ public Multimap serversToPlayers() {
+ return this.serverToPlayersCache.get(SERVERS_TO_PLAYERS_KEY);
+ }
+
+ protected Multimap serversToPlayersBuilder(Object o) {
+ try {
+ return new RedisPipelineTask>(plugin) {
+ private final Set uuids = plugin.proxyDataManager().networkPlayers();
+ private final ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
+
+ @Override
+ public Multimap doPooledPipeline(Pipeline pipeline) {
+ HashMap> responses = new HashMap<>();
+ for (UUID uuid : uuids) {
+ responses.put(uuid, pipeline.hget("redis-bungee::player::" + uuid + "::data", "server"));
+ }
+ pipeline.sync();
+ responses.forEach((uuid, response) -> builder.put(response.get(), uuid));
+ return builder.build();
+ }
+
+ @Override
+ public Multimap clusterPipeline(ClusterPipeline pipeline) {
+ HashMap> responses = new HashMap<>();
+ for (UUID uuid : uuids) {
+ responses.put(uuid, pipeline.hget("redis-bungee::player::" + uuid + "::data", "server"));
+ }
+ pipeline.sync();
+ responses.forEach((uuid, response) -> builder.put(response.get(), uuid));
+ return builder.build();
+ }
+ }.call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/ProxyDataManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/ProxyDataManager.java
new file mode 100644
index 0000000..73a7c14
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/ProxyDataManager.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.gson.AbstractPayloadSerializer;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.DeathPayloadSerializer;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.HeartbeatPayloadSerializer;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.PubSubPayloadSerializer;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.RunCommandPayloadSerializer;
+import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
+import redis.clients.jedis.*;
+import redis.clients.jedis.params.XAddParams;
+import redis.clients.jedis.params.XReadParams;
+import redis.clients.jedis.resps.StreamEntry;
+
+import java.time.Instant;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public abstract class ProxyDataManager implements Runnable, AutoCloseable {
+
+ private static final String STREAM_ID = "redisbungee-stream";
+ private static final int MAX_ENTRIES = 10000;
+
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+
+ private final UnifiedJedis unifiedJedis;
+
+ // data:
+ // Proxy id, heartbeat (unix epoch from instant), players as int
+ private final ConcurrentHashMap heartbeats = new ConcurrentHashMap<>();
+
+ private final String proxyId;
+
+ // This different from proxy id, just to detect if there is duplicate proxy using same proxy id
+ private final UUID dataManagerUUID = UUID.randomUUID();
+
+ protected final RedisBungeePlugin> plugin;
+
+ private final Gson gson = new GsonBuilder().registerTypeAdapter(AbstractPayload.class, new AbstractPayloadSerializer()).registerTypeAdapter(HeartbeatPayload.class, new HeartbeatPayloadSerializer()).registerTypeAdapter(DeathPayload.class, new DeathPayloadSerializer()).registerTypeAdapter(PubSubPayload.class, new PubSubPayloadSerializer()).registerTypeAdapter(RunCommandPayload.class, new RunCommandPayloadSerializer()).create();
+
+ public ProxyDataManager(RedisBungeePlugin> plugin) {
+ this.plugin = plugin;
+ this.proxyId = this.plugin.configuration().getProxyId();
+ this.unifiedJedis = plugin.getSummoner().obtainResource();
+ this.destroyProxyMembers();
+ }
+
+ public abstract Set getLocalOnlineUUIDs();
+
+ public Set getPlayersOn(String proxyId) {
+ checkArgument(proxiesIds().contains(proxyId), proxyId + " is not a valid proxy ID");
+ if (proxyId.equals(this.proxyId)) return this.getLocalOnlineUUIDs();
+ if (!this.heartbeats.containsKey(proxyId)) {
+ return new HashSet<>(); // return empty hashset or null?
+ }
+ return getProxyMembers(proxyId);
+ }
+
+ public List proxiesIds() {
+ return Collections.list(this.heartbeats.keys());
+ }
+
+ public synchronized void sendCommandTo(String proxyToRun, String command) {
+ if (isClosed()) return;
+ publishPayload(new RunCommandPayload(this.proxyId, proxyToRun, command));
+ }
+
+ public synchronized void sendChannelMessage(String channel, String message) {
+ if (isClosed()) return;
+ this.plugin.fireEvent(this.plugin.createPubSubEvent(channel, message));
+ publishPayload(new PubSubPayload(this.proxyId, channel, message));
+ }
+
+ // call every 1 second
+ public synchronized void publishHeartbeat() {
+ if (isClosed()) return;
+ HeartbeatPayload.HeartbeatData heartbeatData = new HeartbeatPayload.HeartbeatData(Instant.now().getEpochSecond(), this.getLocalOnlineUUIDs().size());
+ this.heartbeats.put(this.proxyId(), heartbeatData);
+ publishPayload(new HeartbeatPayload(this.proxyId, heartbeatData));
+ }
+
+ public Set networkPlayers() {
+ try {
+ return new RedisPipelineTask>(this.plugin) {
+ @Override
+ public Set doPooledPipeline(Pipeline pipeline) {
+ HashSet>> responses = new HashSet<>();
+ for (String proxyId : proxiesIds()) {
+ responses.add(pipeline.smembers("redisbungee::proxies::" + proxyId + "::online-players"));
+ }
+ pipeline.sync();
+ HashSet uuids = new HashSet<>();
+ for (Response> response : responses) {
+ for (String stringUUID : response.get()) {
+ uuids.add(UUID.fromString(stringUUID));
+ }
+ }
+ return uuids;
+ }
+
+ @Override
+ public Set clusterPipeline(ClusterPipeline pipeline) {
+ HashSet>> responses = new HashSet<>();
+ for (String proxyId : proxiesIds()) {
+ responses.add(pipeline.smembers("redisbungee::proxies::" + proxyId + "::online-players"));
+ }
+ pipeline.sync();
+ HashSet uuids = new HashSet<>();
+ for (Response> response : responses) {
+ for (String stringUUID : response.get()) {
+ uuids.add(UUID.fromString(stringUUID));
+ }
+ }
+ return uuids;
+ }
+ }.call();
+ } catch (Exception e) {
+ throw new RuntimeException("unable to get network players", e);
+ }
+
+ }
+
+ public int totalNetworkPlayers() {
+ int players = 0;
+ for (HeartbeatPayload.HeartbeatData value : this.heartbeats.values()) {
+ players += value.players();
+ }
+ return players;
+ }
+
+ // Call on close
+ private synchronized void publishDeath() {
+ publishPayload(new DeathPayload(this.proxyId));
+ }
+
+ private void publishPayload(AbstractPayload payload) {
+ Map data = new HashMap<>();
+ data.put("payload", gson.toJson(payload));
+ data.put("data-manager-uuid", this.dataManagerUUID.toString());
+ data.put("class", payload.getClassName());
+ this.unifiedJedis.xadd(STREAM_ID, XAddParams.xAddParams().maxLen(MAX_ENTRIES).id(StreamEntryID.NEW_ENTRY), data);
+ }
+
+
+ private void handleHeartBeat(HeartbeatPayload payload) {
+ String id = payload.senderProxy();
+ if (!heartbeats.containsKey(id)) {
+ plugin.logInfo("Proxy {} has connected", id);
+ }
+ heartbeats.put(id, payload.data());
+ }
+
+
+ // call every 1 minutes
+ public void correctionTask() {
+ // let's check this proxy players
+ Set localOnlineUUIDs = getLocalOnlineUUIDs();
+ Set storedRedisUuids = getProxyMembers(this.proxyId);
+
+ if (!localOnlineUUIDs.equals(storedRedisUuids)) {
+ plugin.logWarn("De-synced playerS set detected correcting....");
+ Set add = new HashSet<>(localOnlineUUIDs);
+ Set remove = new HashSet<>(storedRedisUuids);
+ add.removeAll(storedRedisUuids);
+ remove.removeAll(localOnlineUUIDs);
+ for (UUID uuid : add) {
+ plugin.logWarn("found {} that isn't in the set, adding it to the Corrected set", uuid);
+ }
+ for (UUID uuid : remove) {
+ plugin.logWarn("found {} that does not belong to this proxy removing it from the corrected set", uuid);
+ }
+ try {
+ new RedisPipelineTask(plugin) {
+ @Override
+ public Void doPooledPipeline(Pipeline pipeline) {
+ Set removeString = new HashSet<>();
+ for (UUID uuid : remove) {
+ removeString.add(uuid.toString());
+ }
+ Set addString = new HashSet<>();
+ for (UUID uuid : add) {
+ addString.add(uuid.toString());
+ }
+ pipeline.srem("redisbungee::proxies::" + proxyId() + "::online-players", removeString.toArray(new String[]{}));
+ pipeline.sadd("redisbungee::proxies::" + proxyId() + "::online-players", addString.toArray(new String[]{}));
+ pipeline.sync();
+ return null;
+ }
+
+ @Override
+ public Void clusterPipeline(ClusterPipeline pipeline) {
+ Set removeString = new HashSet<>();
+ for (UUID uuid : remove) {
+ removeString.add(uuid.toString());
+ }
+ Set addString = new HashSet<>();
+ for (UUID uuid : add) {
+ addString.add(uuid.toString());
+ }
+ pipeline.srem("redisbungee::proxies::" + proxyId() + "::online-players", removeString.toArray(new String[]{}));
+ pipeline.sadd("redisbungee::proxies::" + proxyId() + "::online-players", addString.toArray(new String[]{}));
+ pipeline.sync();
+ return null;
+ }
+ }.call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ plugin.logInfo("Player set has been corrected!");
+ }
+
+
+ // handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~10 seconds
+ final Set deadProxies = new HashSet<>();
+ for (Map.Entry stringHeartbeatDataEntry : this.heartbeats.entrySet()) {
+ String id = stringHeartbeatDataEntry.getKey();
+ long heartbeat = stringHeartbeatDataEntry.getValue().heartbeat();
+ if (Instant.now().getEpochSecond() - heartbeat > 10) {
+ deadProxies.add(id);
+ cleanProxy(id);
+ }
+ }
+ try {
+ new RedisPipelineTask(plugin) {
+ @Override
+ public Void doPooledPipeline(Pipeline pipeline) {
+ for (String deadProxy : deadProxies) {
+ pipeline.del("redisbungee::proxies::" + deadProxy + "::online-players");
+ }
+ pipeline.sync();
+ return null;
+ }
+
+ @Override
+ public Void clusterPipeline(ClusterPipeline pipeline) {
+ for (String deadProxy : deadProxies) {
+ pipeline.del("redisbungee::proxies::" + deadProxy + "::online-players");
+ }
+ pipeline.sync();
+ return null;
+ }
+ }.call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void handleProxyDeath(DeathPayload payload) {
+ cleanProxy(payload.senderProxy());
+ }
+
+ private void cleanProxy(String id) {
+ if (id.equals(this.proxyId())) {
+ return;
+ }
+ for (UUID uuid : getProxyMembers(id)) plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
+ plugin.logInfo("Proxy {} has disconnected", id);
+ }
+
+ private void handleChannelMessage(PubSubPayload payload) {
+ String channel = payload.channel();
+ String message = payload.message();
+ this.plugin.fireEvent(this.plugin.createPubSubEvent(channel, message));
+ }
+
+ protected abstract void handlePlatformCommandExecution(String command);
+
+ private void handleCommand(RunCommandPayload payload) {
+ String proxyToRun = payload.proxyToRun();
+ String command = payload.command();
+ if (proxyToRun.equals("allservers") || proxyToRun.equals(this.proxyId())) {
+ handlePlatformCommandExecution(command);
+ }
+ }
+
+
+ public void addPlayer(UUID uuid) {
+ this.unifiedJedis.sadd("redisbungee::proxies::" + this.proxyId + "::online-players", uuid.toString());
+ }
+
+ public void removePlayer(UUID uuid) {
+ this.unifiedJedis.srem("redisbungee::proxies::" + this.proxyId + "::online-players", uuid.toString());
+ }
+
+ private void destroyProxyMembers() {
+ unifiedJedis.del("redisbungee::proxies::" + this.proxyId + "::online-players");
+ }
+
+ private Set getProxyMembers(String proxyId) {
+ Set uuidsStrings = unifiedJedis.smembers("redisbungee::proxies::" + proxyId + "::online-players");
+ HashSet uuids = new HashSet<>();
+ for (String proxyMember : uuidsStrings) {
+ uuids.add(UUID.fromString(proxyMember));
+ }
+ return uuids;
+ }
+
+ private StreamEntryID lastStreamEntryID;
+
+ // polling from stream
+ @Override
+ public void run() {
+ while (!isClosed()) {
+ try {
+ List>> data = unifiedJedis.xread(XReadParams.xReadParams().block(0), Collections.singletonMap(STREAM_ID, lastStreamEntryID != null ? lastStreamEntryID : StreamEntryID.LAST_ENTRY));
+ for (Map.Entry> datum : data) {
+ for (StreamEntry streamEntry : datum.getValue()) {
+ this.lastStreamEntryID = streamEntry.getID();
+ String payloadData = streamEntry.getFields().get("payload");
+ String clazz = streamEntry.getFields().get("class");
+ UUID payloadDataManagerUUID = UUID.fromString(streamEntry.getFields().get("data-manager-uuid"));
+
+ AbstractPayload unknownPayload = (AbstractPayload) gson.fromJson(payloadData, Class.forName(clazz));
+
+ if (unknownPayload.senderProxy().equals(this.proxyId)) {
+ if (!payloadDataManagerUUID.equals(this.dataManagerUUID)) {
+ plugin.logWarn("detected other proxy is using same ID! {} this can cause issues, please shutdown this proxy and change the id!", this.proxyId);
+ }
+ break;
+ }
+ if (unknownPayload instanceof HeartbeatPayload payload) {
+ handleHeartBeat(payload);
+ } else if (unknownPayload instanceof DeathPayload payload) {
+ handleProxyDeath(payload);
+ } else if (unknownPayload instanceof RunCommandPayload payload) {
+ handleCommand(payload);
+ } else if (unknownPayload instanceof PubSubPayload payload) {
+ handleChannelMessage(payload);
+ } else {
+ plugin.logWarn("got unknown data manager payload: {}", unknownPayload.getClassName());
+ }
+ }
+ }
+ } catch (Exception e) {
+ this.plugin.logFatal("an error has occurred in the stream", e);
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ closed.set(true);
+ this.publishDeath();
+ this.heartbeats.clear();
+ this.destroyProxyMembers();
+ }
+
+ public boolean isClosed() {
+ return closed.get();
+ }
+
+ public String proxyId() {
+ return proxyId;
+ }
+
+ public UnifiedJedis unifiedJedis() {
+ return unifiedJedis;
+ }
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PubSubListener.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PubSubListener.java
deleted file mode 100644
index cd19d71..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PubSubListener.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api;
-
-import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisCluster;
-import redis.clients.jedis.UnifiedJedis;
-import redis.clients.jedis.exceptions.JedisConnectionException;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-public class PubSubListener implements Runnable {
- private JedisPubSubHandler jpsh;
- private final Set addedChannels = new HashSet();
-
- private final RedisBungeePlugin> plugin;
-
- public PubSubListener(RedisBungeePlugin> plugin) {
- this.plugin = plugin;
- }
-
- @Override
- public void run() {
- RedisTask subTask = new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- jpsh = new JedisPubSubHandler(plugin);
- addedChannels.add("redisbungee-" + plugin.getConfiguration().getProxyId());
- addedChannels.add("redisbungee-allservers");
- addedChannels.add("redisbungee-data");
- unifiedJedis.subscribe(jpsh, addedChannels.toArray(new String[0]));
- return null;
- }
- };
-
- try {
- subTask.execute();
- } catch (Exception e) {
- plugin.logWarn("PubSub error, attempting to recover in 5 secs.");
- plugin.executeAsyncAfter(this, TimeUnit.SECONDS, 5);
- }
- }
-
- 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/api/RedisBungeePlugin.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/RedisBungeePlugin.java
index a0e2471..3c827a0 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/RedisBungeePlugin.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/RedisBungeePlugin.java
@@ -10,28 +10,16 @@
package com.imaginarycode.minecraft.redisbungee.api;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.EventsPlatform;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
-import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
-import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil;
-import com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
-import redis.clients.jedis.Protocol;
-import redis.clients.jedis.UnifiedJedis;
-import redis.clients.jedis.exceptions.JedisConnectionException;
import java.net.InetAddress;
-import java.util.*;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
-import static com.google.common.base.Preconditions.checkArgument;
-
/**
* This Class has all internal methods needed by every redis bungee plugin, and it can be used to implement another platforms than bungeecord or another forks of RedisBungee
@@ -51,225 +39,54 @@ public interface RedisBungeePlugin extends EventsPlatform {
}
+ void logInfo(String msg);
+
+ void logInfo(String format, Object... object);
+
+ void logWarn(String msg);
+
+ void logWarn(String format, Object... object);
+
+ void logFatal(String msg);
+
+ void logFatal(String format, Throwable throwable);
+
+ RedisBungeeConfiguration configuration();
+
Summoner> getSummoner();
- RedisBungeeConfiguration getConfiguration();
-
- int getCount();
-
- default int getCurrentCount() {
- return new RedisTask(this) {
- @Override
- public Long unifiedJedisTask(UnifiedJedis unifiedJedis) {
- long total = 0;
- long redisTime = getRedisTime(unifiedJedis);
- Map heartBeats = unifiedJedis.hgetAll("heartbeats");
- for (Map.Entry stringStringEntry : heartBeats.entrySet()) {
- String k = stringStringEntry.getKey();
- String v = stringStringEntry.getValue();
-
- long heartbeatTime = Long.parseLong(v);
- if (heartbeatTime + RedisUtil.PROXY_TIMEOUT >= redisTime) {
- total = total + unifiedJedis.scard("proxy:" + k + ":usersOnline");
- }
- }
- return total;
- }
- }.execute().intValue();
- }
-
- Set getLocalPlayersAsUuidStrings();
-
- AbstractDataManager getDataManager();
-
- default Set getPlayers() {
- return new RedisTask>(this) {
- @Override
- public Set unifiedJedisTask(UnifiedJedis unifiedJedis) {
- ImmutableSet.Builder setBuilder = ImmutableSet.builder();
- try {
- List keys = new ArrayList<>();
- for (String i : getProxiesIds()) {
- keys.add("proxy:" + i + ":usersOnline");
- }
- if (!keys.isEmpty()) {
- Set users = unifiedJedis.sunion(keys.toArray(new String[0]));
- 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!
- logFatal("Unable to get connection from pool - did your Redis server go away?");
- throw new RuntimeException("Unable to get all players online", e);
- }
- return setBuilder.build();
- }
- }.execute();
- }
+ RedisBungeeMode getRedisBungeeMode();
AbstractRedisBungeeAPI getAbstractRedisBungeeApi();
+ ProxyDataManager proxyDataManager();
+
+ PlayerDataManager playerDataManager();
+
UUIDTranslator getUuidTranslator();
- Multimap serverToPlayersCache();
+ boolean isOnlineMode();
- default Multimap serversToPlayers() {
- return new RedisTask>(this) {
- @Override
- public Multimap unifiedJedisTask(UnifiedJedis unifiedJedis) {
- ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
- for (String serverId : getProxiesIds()) {
- Set players = unifiedJedis.smembers("proxy:" + serverId + ":usersOnline");
- for (String player : players) {
- String playerServer = unifiedJedis.hget("player:" + player, "server");
- if (playerServer == null) {
- continue;
- }
- builder.put(playerServer, UUID.fromString(player));
- }
- }
- return builder.build();
- }
- }.execute();
- }
+ public P getPlayer(UUID uuid);
- default Set getPlayersOnProxy(String proxyId) {
- checkArgument(getProxiesIds().contains(proxyId), proxyId + " is not a valid proxy ID");
- return new RedisTask>(this) {
- @Override
- public Set unifiedJedisTask(UnifiedJedis unifiedJedis) {
- Set users = unifiedJedis.smembers("proxy:" + proxyId + ":usersOnline");
- ImmutableSet.Builder builder = ImmutableSet.builder();
- for (String user : users) {
- builder.add(UUID.fromString(user));
- }
- return builder.build();
- }
- }.execute();
- }
+ public P getPlayer(String name);
- default void sendProxyCommand(String proxyId, String command) {
- checkArgument(getProxiesIds().contains(proxyId) || proxyId.equals("allservers"), "proxyId is invalid");
- sendChannelMessage("redisbungee-" + proxyId, command);
- }
+ public UUID getPlayerUUID(String player);
- List getProxiesIds();
- default List getCurrentProxiesIds(boolean lagged) {
- return new RedisTask>(this) {
- @Override
- public List unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- long time = getRedisTime(unifiedJedis);
- ImmutableList.Builder servers = ImmutableList.builder();
- Map heartbeats = unifiedJedis.hgetAll("heartbeats");
- for (Map.Entry entry : heartbeats.entrySet()) {
- try {
- long stamp = Long.parseLong(entry.getValue());
- if (lagged ? time >= stamp + RedisUtil.PROXY_TIMEOUT : time <= stamp + RedisUtil.PROXY_TIMEOUT) {
- servers.add(entry.getKey());
- } else if (time > stamp + RedisUtil.PROXY_TIMEOUT) {
- logWarn(entry.getKey() + " is " + (time - stamp) + " seconds behind! (Time not synchronized or server down?) and was removed from heartbeat.");
- unifiedJedis.hdel("heartbeats", entry.getKey());
- }
- } catch (NumberFormatException ignored) {
- }
- }
- return servers.build();
- } catch (JedisConnectionException e) {
- logFatal("Unable to fetch server IDs");
- e.printStackTrace();
- return Collections.singletonList(getConfiguration().getProxyId());
- }
- }
- }.execute();
- }
+ public String getPlayerName(UUID player);
- PubSubListener getPubSubListener();
+ boolean handlePlatformKick(UUID uuid, String message);
- default void sendChannelMessage(String channel, String message) {
- new RedisTask(this) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- unifiedJedis.publish(channel, message);
- } catch (JedisConnectionException e) {
- // Redis server has disappeared!
- logFatal("Unable to get connection from pool - did your Redis server go away?");
- throw new RuntimeException("Unable to publish channel message", e);
- }
- return null;
- }
- }.execute();
- }
+ public String getPlayerServerName(P player);
+
+ public boolean isPlayerOnAServer(P player);
+
+ public InetAddress getPlayerIp(P player);
void executeAsync(Runnable runnable);
void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
- boolean isOnlineMode();
-
- void logInfo(String msg);
-
- void logWarn(String msg);
-
- void logFatal(String msg);
-
- 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);
-
- default void sendProxyCommand(String cmd) {
- sendProxyCommand(getConfiguration().getProxyId(), cmd);
- }
-
- default Long getRedisTime(UnifiedJedis unifiedJedis) {
- List data = (List) unifiedJedis.sendCommand(Protocol.Command.TIME);
- List times = new ArrayList<>();
- data.forEach((o) -> times.add(new String((byte[])o)));
- return getRedisTime(times);
- }
- default long getRedisTime(List timeRes) {
- return Long.parseLong(timeRes.get(0));
- }
-
- default void kickPlayer(UUID playerUniqueId, String message) {
- // first handle on origin proxy if player not found publish the payload
- if (!getDataManager().handleKick(playerUniqueId, message)) {
- new RedisTask(this) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- PayloadUtils.kickPlayerPayload(playerUniqueId, message, unifiedJedis);
- return null;
- }
- }.execute();
- }
- }
-
- default void kickPlayer(String playerName, String message) {
- // fetch the uuid from name
- UUID playerUUID = getUuidTranslator().getTranslatedUuid(playerName, true);
- kickPlayer(playerUUID, message);
- }
-
- RedisBungeeMode getRedisBungeeMode();
-
- void updateProxiesIds();
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/RedisBungeeConfiguration.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/RedisBungeeConfiguration.java
index 2e595f4..e10c9d0 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/RedisBungeeConfiguration.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/RedisBungeeConfiguration.java
@@ -12,11 +12,9 @@ package com.imaginarycode.minecraft.redisbungee.api.config;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
-import java.util.HashMap;
import java.util.List;
public class RedisBungeeConfiguration {
@@ -47,6 +45,7 @@ public class RedisBungeeConfiguration {
this.overrideBungeeCommands = overrideBungeeCommands;
this.restoreOldKickBehavior = restoreOldKickBehavior;
}
+
public String getProxyId() {
return proxyId;
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/events/EventsPlatform.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/events/EventsPlatform.java
index 099c075..79dabfa 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/events/EventsPlatform.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/events/EventsPlatform.java
@@ -17,7 +17,6 @@ import java.util.UUID;
*
* @author Ham1255
* @since 0.7.0
- *
*/
public interface EventsPlatform {
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/AbstractPayload.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/AbstractPayload.java
new file mode 100644
index 0000000..e41ee5f
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/AbstractPayload.java
@@ -0,0 +1,24 @@
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads;
+
+public abstract class AbstractPayload {
+
+ private final String senderProxy;
+
+ public AbstractPayload(String proxyId) {
+ this.senderProxy = proxyId;
+ }
+
+ public AbstractPayload(String senderProxy, String className) {
+ this.senderProxy = senderProxy;
+ }
+
+ public String senderProxy() {
+ return senderProxy;
+ }
+
+ public String getClassName() {
+ return getClass().getName();
+ }
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/gson/AbstractPayloadSerializer.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/gson/AbstractPayloadSerializer.java
new file mode 100644
index 0000000..6769ff2
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/gson/AbstractPayloadSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.gson;
+
+import com.google.gson.*;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+
+import java.lang.reflect.Type;
+
+public class AbstractPayloadSerializer implements JsonSerializer, JsonDeserializer {
+
+
+ @Override
+ public AbstractPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+ return new AbstractPayload(jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) {
+ };
+ }
+
+ @Override
+ public JsonElement serialize(AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
+ return jsonObject;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/DeathPayload.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/DeathPayload.java
new file mode 100644
index 0000000..399071a
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/DeathPayload.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
+
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+
+public class DeathPayload extends AbstractPayload {
+ public DeathPayload(String proxyId) {
+ super(proxyId);
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/HeartbeatPayload.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/HeartbeatPayload.java
new file mode 100644
index 0000000..02268fd
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/HeartbeatPayload.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
+
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+
+public class HeartbeatPayload extends AbstractPayload {
+
+ public record HeartbeatData(long heartbeat, int players) {
+
+ }
+
+ private final HeartbeatData data;
+
+ public HeartbeatPayload(String proxyId, HeartbeatData data) {
+ super(proxyId);
+ this.data = data;
+ }
+
+ public HeartbeatData data() {
+ return data;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/PubSubPayload.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/PubSubPayload.java
new file mode 100644
index 0000000..eaa9092
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/PubSubPayload.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
+
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+
+public class PubSubPayload extends AbstractPayload {
+
+ private final String channel;
+ private final String message;
+
+
+ public PubSubPayload(String proxyId, String channel, String message) {
+ super(proxyId);
+ this.channel = channel;
+ this.message = message;
+ }
+
+ public String channel() {
+ return channel;
+ }
+
+ public String message() {
+ return message;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/RunCommandPayload.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/RunCommandPayload.java
new file mode 100644
index 0000000..6374e5c
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/RunCommandPayload.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
+
+import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
+
+public class RunCommandPayload extends AbstractPayload {
+
+
+ private final String proxyToRun;
+
+ private final String command;
+
+
+ public RunCommandPayload(String proxyId, String proxyToRun, String command) {
+ super(proxyId);
+ this.proxyToRun = proxyToRun;
+ this.command = command;
+ }
+
+ public String proxyToRun() {
+ return proxyToRun;
+ }
+
+ public String command() {
+ return command;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/DeathPayloadSerializer.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/DeathPayloadSerializer.java
new file mode 100644
index 0000000..d77dd51
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/DeathPayloadSerializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
+
+import com.google.gson.*;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
+
+import java.lang.reflect.Type;
+
+public class DeathPayloadSerializer implements JsonSerializer, JsonDeserializer {
+
+ private static final Gson gson = new Gson();
+
+
+ @Override
+ public DeathPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+ String senderProxy = jsonObject.get("proxy").getAsString();
+ return new DeathPayload(senderProxy);
+ }
+
+ @Override
+ public JsonElement serialize(DeathPayload src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
+ return jsonObject;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/HeartbeatPayloadSerializer.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/HeartbeatPayloadSerializer.java
new file mode 100644
index 0000000..1f301f2
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/HeartbeatPayloadSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
+
+import com.google.gson.*;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
+
+import java.lang.reflect.Type;
+
+public class HeartbeatPayloadSerializer implements JsonSerializer, JsonDeserializer {
+
+
+ @Override
+ public HeartbeatPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+ String senderProxy = jsonObject.get("proxy").getAsString();
+ long heartbeat = jsonObject.get("heartbeat").getAsLong();
+ int players = jsonObject.get("players").getAsInt();
+ return new HeartbeatPayload(senderProxy, new HeartbeatPayload.HeartbeatData(heartbeat, players));
+ }
+
+ @Override
+ public JsonElement serialize(HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
+ jsonObject.add("heartbeat", new JsonPrimitive(src.data().heartbeat()));
+ jsonObject.add("players", new JsonPrimitive(src.data().players()));
+ return jsonObject;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/PubSubPayloadSerializer.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/PubSubPayloadSerializer.java
new file mode 100644
index 0000000..01d66a5
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/PubSubPayloadSerializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
+
+import com.google.gson.*;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
+
+import java.lang.reflect.Type;
+
+public class PubSubPayloadSerializer implements JsonSerializer, JsonDeserializer {
+
+ private static final Gson gson = new Gson();
+
+
+ @Override
+ public PubSubPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+ String senderProxy = jsonObject.get("proxy").getAsString();
+ String channel = jsonObject.get("channel").getAsString();
+ String message = jsonObject.get("message").getAsString();
+ return new PubSubPayload(senderProxy, channel, message);
+ }
+
+ @Override
+ public JsonElement serialize(PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
+ jsonObject.add("channel", new JsonPrimitive(src.channel()));
+ jsonObject.add("message", context.serialize(src.message()));
+ return jsonObject;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/RunCommandPayloadSerializer.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/RunCommandPayloadSerializer.java
new file mode 100644
index 0000000..2a7de33
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/payloads/proxy/gson/RunCommandPayloadSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
+
+import com.google.gson.*;
+import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
+
+import java.lang.reflect.Type;
+
+public class RunCommandPayloadSerializer implements JsonSerializer, JsonDeserializer {
+
+
+ @Override
+ public RunCommandPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+ String senderProxy = jsonObject.get("proxy").getAsString();
+ String proxyToRun = jsonObject.get("proxy-to-run").getAsString();
+ String command = jsonObject.get("command").getAsString();
+ return new RunCommandPayload(senderProxy, proxyToRun, command);
+ }
+
+ @Override
+ public JsonElement serialize(RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
+ jsonObject.add("proxy-to-run", new JsonPrimitive(src.proxyToRun()));
+ jsonObject.add("command", context.serialize(src.command()));
+ return jsonObject;
+ }
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/JedisClusterSummoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/JedisClusterSummoner.java
index 99d8e19..14c2514 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/JedisClusterSummoner.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/JedisClusterSummoner.java
@@ -17,7 +17,7 @@ import java.io.IOException;
import java.time.Duration;
public class JedisClusterSummoner implements Summoner {
- public final ClusterConnectionProvider clusterConnectionProvider;
+ private final ClusterConnectionProvider clusterConnectionProvider;
public JedisClusterSummoner(ClusterConnectionProvider clusterConnectionProvider) {
this.clusterConnectionProvider = clusterConnectionProvider;
@@ -35,6 +35,8 @@ public class JedisClusterSummoner implements Summoner {
@Override
public JedisCluster obtainResource() {
- return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(30000));
+ return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(10));
}
+
+
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/NotClosableJedisCluster.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/NotClosableJedisCluster.java
index 84eb85a..5e09859 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/NotClosableJedisCluster.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/NotClosableJedisCluster.java
@@ -11,9 +11,7 @@
package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisCluster;
-import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.ClusterConnectionProvider;
-import redis.clients.jedis.providers.PooledConnectionProvider;
import java.time.Duration;
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/Summoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/Summoner.java
index 6b511e7..36beac5 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/Summoner.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/summoners/Summoner.java
@@ -10,6 +10,8 @@
package com.imaginarycode.minecraft.redisbungee.api.summoners;
+import redis.clients.jedis.UnifiedJedis;
+
import java.io.Closeable;
@@ -18,9 +20,8 @@ import java.io.Closeable;
*
* @author Ham1255
* @since 0.7.0
- *
*/
-public interface Summoner extends Closeable {
+public interface Summoner
extends Closeable {
P obtainResource();
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/HeartbeatTask.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/HeartbeatTask.java
deleted file mode 100644
index 669ba8c..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/HeartbeatTask.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api.tasks;
-
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisCluster;
-import redis.clients.jedis.UnifiedJedis;
-import redis.clients.jedis.exceptions.JedisConnectionException;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class HeartbeatTask extends RedisTask{
-
- public final static TimeUnit REPEAT_INTERVAL_TIME_UNIT = TimeUnit.SECONDS;
- public final static int INTERVAL = 1;
- private final AtomicInteger globalPlayerCount;
-
- public HeartbeatTask(RedisBungeePlugin> plugin, AtomicInteger globalPlayerCount) {
- super(plugin);
- this.globalPlayerCount = globalPlayerCount;
- }
-
-
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- long redisTime = plugin.getRedisTime(unifiedJedis);
- unifiedJedis.hset("heartbeats", plugin.getConfiguration().getProxyId(), String.valueOf(redisTime));
- } catch (JedisConnectionException e) {
- // Redis server has disappeared!
- plugin.logFatal("Unable to update heartbeat - did your Redis server go away?");
- e.printStackTrace();
- return null;
- }
- try {
- plugin.updateProxiesIds();
- globalPlayerCount.set(plugin.getCurrentCount());
- } catch (Throwable e) {
- plugin.logFatal("Unable to update data - did your Redis server go away?");
- e.printStackTrace();
- }
- return null;
- }
-
-
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/InitialUtils.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/InitialUtils.java
deleted file mode 100644
index 8a2986f..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/InitialUtils.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api.tasks;
-
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil;
-import redis.clients.jedis.Protocol;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-public class InitialUtils {
-
- public static void checkRedisVersion(RedisBungeePlugin> plugin) {
- new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- // This is more portable than INFO
- String info = new String((byte[]) unifiedJedis.sendCommand(Protocol.Command.INFO));
- for (String s : info.split("\r\n")) {
- if (s.startsWith("redis_version:")) {
- String version = s.split(":")[1];
- plugin.logInfo("Redis server version: " + version);
- if (!RedisUtil.isRedisVersionRight(version)) {
- plugin.logFatal("Your version of Redis (" + version + ") is not at least version 3.0 RedisBungee requires a newer version of Redis.");
- throw new RuntimeException("Unsupported Redis version detected");
- }
- long uuidCacheSize = unifiedJedis.hlen("uuid-cache");
- if (uuidCacheSize > 750000) {
- plugin.logInfo("Looks like you have a really big UUID cache! Run https://github.com/ProxioDev/Brains");
- }
- break;
- }
- }
- return null;
- }
- }.execute();
- }
-
-
- public static void checkIfRecovering(RedisBungeePlugin> plugin, Path dataFolder) {
- new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- Path crashFile = dataFolder.resolve("restarted_from_crash.txt");
- if (Files.exists(crashFile)) {
- try {
- Files.delete(crashFile);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- plugin.logInfo("crash file was deleted continuing RedisBungee startup ");
- } else if (unifiedJedis.hexists("heartbeats", plugin.getConfiguration().getProxyId())) {
- try {
- long value = Long.parseLong(unifiedJedis.hget("heartbeats", plugin.getConfiguration().getProxyId()));
- long redisTime = plugin.getRedisTime(unifiedJedis);
-
- if (redisTime < value + RedisUtil.PROXY_TIMEOUT) {
- logImposter(plugin);
- throw new RuntimeException("Possible impostor instance!");
- }
- } catch (NumberFormatException ignored) {
- }
- }
- return null;
- }
- }.execute();
- }
-
- private static void logImposter(RedisBungeePlugin> plugin) {
- plugin.logFatal("You have launched a possible impostor Velocity / Bungeecord instance. Another instance is already running.");
- plugin.logFatal("For data consistency reasons, RedisBungee will now disable itself.");
- plugin.logFatal("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.");
- }
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/IntegrityCheckTask.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/IntegrityCheckTask.java
deleted file mode 100644
index c13742e..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/IntegrityCheckTask.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api.tasks;
-
-import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-public abstract class IntegrityCheckTask extends RedisTask {
-
- public static int INTERVAL = 30;
- public static TimeUnit TIMEUNIT = TimeUnit.SECONDS;
-
-
- public IntegrityCheckTask(RedisBungeePlugin> plugin) {
- super(plugin);
- }
-
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- Set players = plugin.getLocalPlayersAsUuidStrings();
- Set playersInRedis = unifiedJedis.smembers("proxy:" + plugin.getConfiguration().getProxyId() + ":usersOnline");
- List lagged = plugin.getCurrentProxiesIds(true);
-
- // Clean up lagged players.
- for (String s : lagged) {
- Set laggedPlayers = unifiedJedis.smembers("proxy:" + s + ":usersOnline");
- unifiedJedis.del("proxy:" + s + ":usersOnline");
- if (!laggedPlayers.isEmpty()) {
- plugin.logInfo("Cleaning up lagged proxy " + s + " (" + laggedPlayers.size() + " players)...");
- for (String laggedPlayer : laggedPlayers) {
- PlayerUtils.cleanUpPlayer(laggedPlayer, unifiedJedis, true);
- }
- }
- }
-
- 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 : plugin.getProxiesIds()) {
- if (proxyId.equals(plugin.getConfiguration().getProxyId())) continue;
- if (unifiedJedis.sismember("proxy:" + proxyId + ":usersOnline", member)) {
- // Just clean up the set.
- found = true;
- break;
- }
- }
- if (!found) {
- PlayerUtils.cleanUpPlayer(member, unifiedJedis, false);
- plugin.logWarn("Player found in set that was not found locally and globally: " + member);
- } else {
- unifiedJedis.srem("proxy:" + plugin.getConfiguration().getProxyId() + ":usersOnline", member);
- plugin.logWarn("Player found in set that was not found locally, but is on another proxy: " + member);
- }
- }
- // due unifiedJedis does not support pipelined.
- //Pipeline pipeline = jedis.pipelined();
-
- for (String player : absentInRedis) {
- // Player not online according to Redis but not BungeeCord.
- handlePlatformPlayer(player, unifiedJedis);
- }
- } catch (Throwable e) {
- plugin.logFatal("Unable to fix up stored player data");
- e.printStackTrace();
- }
- return null;
- }
-
-
- public abstract void handlePlatformPlayer(String player, UnifiedJedis unifiedJedis);
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisPipelineTask.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisPipelineTask.java
new file mode 100644
index 0000000..21a5d29
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisPipelineTask.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.tasks;
+
+import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import redis.clients.jedis.*;
+
+public abstract class RedisPipelineTask extends RedisTask {
+
+
+ public RedisPipelineTask(AbstractRedisBungeeAPI api) {
+ super(api);
+ }
+
+ public RedisPipelineTask(RedisBungeePlugin> plugin) {
+ super(plugin);
+ }
+
+
+ @Override
+ public T unifiedJedisTask(UnifiedJedis unifiedJedis) {
+ if (unifiedJedis instanceof JedisPooled pooled) {
+ try (Pipeline pipeline = pooled.pipelined()) {
+ return doPooledPipeline(pipeline);
+ }
+ } else if (unifiedJedis instanceof JedisCluster jedisCluster) {
+ try (ClusterPipeline pipeline = jedisCluster.pipelined()) {
+ return clusterPipeline(pipeline);
+ }
+ }
+
+ return null;
+ }
+
+ public abstract T doPooledPipeline(Pipeline pipeline);
+
+ public abstract T clusterPipeline(ClusterPipeline pipeline);
+
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisTask.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisTask.java
index eb1b416..9a6da17 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisTask.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/RedisTask.java
@@ -11,11 +11,11 @@
package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import redis.clients.jedis.UnifiedJedis;
import java.util.concurrent.Callable;
@@ -27,23 +27,22 @@ import java.util.concurrent.Callable;
public abstract class RedisTask implements Runnable, Callable {
protected final Summoner> summoner;
- protected final AbstractRedisBungeeAPI api;
- protected RedisBungeePlugin> plugin;
+
+ protected final RedisBungeeMode mode;
@Override
public V call() throws Exception {
- return execute();
+ return this.execute();
}
public RedisTask(AbstractRedisBungeeAPI api) {
- this.api = api;
this.summoner = api.getSummoner();
+ this.mode = api.getMode();
}
public RedisTask(RedisBungeePlugin> plugin) {
- this.plugin = plugin;
- this.api = plugin.getAbstractRedisBungeeApi();
- this.summoner = api.getSummoner();
+ this.summoner = plugin.getSummoner();
+ this.mode = plugin.getRedisBungeeMode();
}
public abstract V unifiedJedisTask(UnifiedJedis unifiedJedis);
@@ -53,22 +52,16 @@ public abstract class RedisTask implements Runnable, Callable {
this.execute();
}
- public V execute(){
+ public V execute() {
// JedisCluster, JedisPooled in fact is just UnifiedJedis does not need new instance since its single instance anyway.
- if (api.getMode() == RedisBungeeMode.SINGLE) {
+ if (mode == RedisBungeeMode.SINGLE) {
JedisPooledSummoner jedisSummoner = (JedisPooledSummoner) summoner;
return this.unifiedJedisTask(jedisSummoner.obtainResource());
- } else if (api.getMode() == RedisBungeeMode.CLUSTER) {
+ } else if (mode == RedisBungeeMode.CLUSTER) {
JedisClusterSummoner jedisClusterSummoner = (JedisClusterSummoner) summoner;
return this.unifiedJedisTask(jedisClusterSummoner.obtainResource());
}
return null;
}
- public RedisBungeePlugin> getPlugin() {
- if (plugin == null) {
- throw new NullPointerException("Plugin is null in the task");
- }
- return plugin;
- }
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/ShutdownUtils.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/ShutdownUtils.java
deleted file mode 100644
index a3fdbcc..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/ShutdownUtils.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee.api.tasks;
-
-import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisCluster;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.util.Set;
-
-public class ShutdownUtils {
-
- public static void shutdownCleanup(RedisBungeePlugin> plugin) {
- new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- unifiedJedis.hdel("heartbeats", plugin.getConfiguration().getProxyId());
- if (unifiedJedis.scard("proxy:" + plugin.getConfiguration().getProxyId() + ":usersOnline") > 0) {
- Set players = unifiedJedis.smembers("proxy:" + plugin.getConfiguration().getProxyId() + ":usersOnline");
- for (String member : players)
- PlayerUtils.cleanUpPlayer(member, unifiedJedis, true);
- }
- return null;
- }
- }.execute();
- }
-
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/InitialUtils.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/InitialUtils.java
new file mode 100644
index 0000000..6815770
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/InitialUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee.api.util;
+
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
+import redis.clients.jedis.Protocol;
+import redis.clients.jedis.UnifiedJedis;
+
+
+public class InitialUtils {
+
+ public static void checkRedisVersion(RedisBungeePlugin> plugin) {
+ new RedisTask(plugin) {
+ @Override
+ public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
+ // This is more portable than INFO
+ String info = new String((byte[]) unifiedJedis.sendCommand(Protocol.Command.INFO));
+ for (String s : info.split("\r\n")) {
+ if (s.startsWith("redis_version:")) {
+ String version = s.split(":")[1];
+ plugin.logInfo("Redis server version: " + version);
+ if (!RedisUtil.isRedisVersionRight(version)) {
+ plugin.logFatal("Your version of Redis (" + version + ") is not at least version 3.0 RedisBungee requires a newer version of Redis.");
+ throw new RuntimeException("Unsupported Redis version detected");
+ }
+ long uuidCacheSize = unifiedJedis.hlen("uuid-cache");
+ if (uuidCacheSize > 750000) {
+ plugin.logInfo("Looks like you have a really big UUID cache! Run https://github.com/ProxioDev/Brains");
+ }
+ break;
+ }
+ }
+ return null;
+ }
+ }.execute();
+ }
+
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/RedisUtil.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/RedisUtil.java
index 9e4bd92..2e00c1e 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/RedisUtil.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/RedisUtil.java
@@ -5,6 +5,7 @@ import com.google.common.annotations.VisibleForTesting;
@VisibleForTesting
public class RedisUtil {
public final static int PROXY_TIMEOUT = 30;
+
public static boolean isRedisVersionRight(String redisVersion) {
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/io/IOUtil.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/io/IOUtil.java
deleted file mode 100644
index fa4290e..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/io/IOUtil.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.imaginarycode.minecraft.redisbungee.api.util.io;
-
-import com.google.common.io.ByteStreams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-
-public class IOUtil {
- public static String readInputStreamAsString(InputStream is) {
- String string;
- try {
- string = new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- return string;
- }
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/payload/PayloadUtils.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/payload/PayloadUtils.java
deleted file mode 100644
index 36e9b78..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/payload/PayloadUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.imaginarycode.minecraft.redisbungee.api.util.payload;
-
-import com.google.gson.Gson;
-import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
-import com.imaginarycode.minecraft.redisbungee.api.AbstractDataManager;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.net.InetAddress;
-import java.util.UUID;
-
-public class PayloadUtils {
- private static final Gson gson = new Gson();
-
- public static void playerJoinPayload(UUID uuid, UnifiedJedis unifiedJedis, InetAddress inetAddress) {
- unifiedJedis.publish("redisbungee-data", gson.toJson(new AbstractDataManager.DataManagerMessage<>(
- uuid, AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId(), AbstractDataManager.DataManagerMessage.Action.JOIN,
- new AbstractDataManager.LoginPayload(inetAddress))));
- }
-
-
- public static void playerQuitPayload(String uuid, UnifiedJedis unifiedJedis, long timestamp) {
- unifiedJedis.publish("redisbungee-data", gson.toJson(new AbstractDataManager.DataManagerMessage<>(
- UUID.fromString(uuid), AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId(), AbstractDataManager.DataManagerMessage.Action.LEAVE,
- new AbstractDataManager.LogoutPayload(timestamp))));
- }
-
-
-
- public static void playerServerChangePayload(UUID uuid, UnifiedJedis unifiedJedis, String newServer, String oldServer) {
- unifiedJedis.publish("redisbungee-data", gson.toJson(new AbstractDataManager.DataManagerMessage<>(
- uuid, AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId(), AbstractDataManager.DataManagerMessage.Action.SERVER_CHANGE,
- new AbstractDataManager.ServerChangePayload(newServer, oldServer))));
- }
-
-
- public static void kickPlayerPayload(UUID uuid, String message, UnifiedJedis unifiedJedis) {
- unifiedJedis.publish("redisbungee-data", gson.toJson(new AbstractDataManager.DataManagerMessage<>(
- uuid, AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId(), AbstractDataManager.DataManagerMessage.Action.KICK,
- new AbstractDataManager.KickPayload(message))));
- }
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/player/PlayerUtils.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/player/PlayerUtils.java
deleted file mode 100644
index 820ad34..0000000
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/player/PlayerUtils.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.imaginarycode.minecraft.redisbungee.api.util.player;
-
-import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.net.InetAddress;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-import static com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils.playerJoinPayload;
-import static com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils.playerQuitPayload;
-
-public class PlayerUtils {
-
- public static void cleanUpPlayer(String uuid, UnifiedJedis rsc, boolean firePayload) {
- final long timestamp = System.currentTimeMillis();
- final boolean isKickedFromOtherLocation = isKickedOtherLocation(uuid, rsc);
- rsc.srem("proxy:" + AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId() + ":usersOnline", uuid);
- if (!isKickedFromOtherLocation) {
- rsc.hdel("player:" + uuid, "server", "ip", "proxy");
- rsc.hset("player:" + uuid, "online", String.valueOf(timestamp));
- }
- if (firePayload && !isKickedFromOtherLocation) {
- playerQuitPayload(uuid, rsc, timestamp);
- }
- }
-
- public static void setKickedOtherLocation(String uuid, UnifiedJedis unifiedJedis) {
- // set anything for sake of exists check. then expire it after 2 seconds. should be great?
- unifiedJedis.set("kicked-other-location::" + uuid, "0");
- unifiedJedis.expire("kicked-other-location::" + uuid, 2);
- }
-
- public static boolean isKickedOtherLocation(String uuid, UnifiedJedis unifiedJedis) {
- return unifiedJedis.exists("kicked-other-location::" + uuid);
- }
-
-
- public static void createPlayer(UUID uuid, UnifiedJedis unifiedJedis, String currentServer, InetAddress hostname, boolean fireEvent) {
- final boolean isKickedFromOtherLocation = isKickedOtherLocation(uuid.toString(), unifiedJedis);
- Map playerData = new HashMap<>(4);
- playerData.put("online", "0");
- playerData.put("ip", hostname.getHostName());
- playerData.put("proxy", AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId());
- if (currentServer != null) {
- playerData.put("server", currentServer);
- }
- unifiedJedis.sadd("proxy:" + AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId() + ":usersOnline", uuid.toString());
- unifiedJedis.hset("player:" + uuid, playerData);
- if (fireEvent && !isKickedFromOtherLocation) {
- playerJoinPayload(uuid, unifiedJedis, hostname);
- }
- }
-
-
-}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/Serializations.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/MultiMapSerialization.java
similarity index 97%
rename from RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/Serializations.java
rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/MultiMapSerialization.java
index 7ee9cc5..71df20b 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/Serializations.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/serialize/MultiMapSerialization.java
@@ -7,7 +7,7 @@ import com.google.common.io.ByteArrayDataOutput;
import java.util.Collection;
import java.util.Map;
-public class Serializations {
+public class MultiMapSerialization {
public static void serializeMultiset(Multiset collection, ByteArrayDataOutput output) {
output.writeInt(collection.elementSet().size());
@@ -36,4 +36,5 @@ public class Serializations {
output.writeUTF(o.toString());
}
}
+
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/NameFetcher.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/NameFetcher.java
index 69eb689..3bcd38c 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/NameFetcher.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/NameFetcher.java
@@ -22,38 +22,38 @@ import java.util.List;
import java.util.UUID;
public class NameFetcher {
- private static OkHttpClient httpClient;
- private static final Gson gson = new Gson();
+ private static OkHttpClient httpClient;
+ private static final Gson gson = new Gson();
- public static void setHttpClient(OkHttpClient httpClient) {
- NameFetcher.httpClient = httpClient;
- }
+ public static void setHttpClient(OkHttpClient httpClient) {
+ NameFetcher.httpClient = httpClient;
+ }
- public static List nameHistoryFromUuid(UUID uuid) throws IOException {
- String name = getName(uuid);
- if (name == null) return Collections.emptyList();
- return Collections.singletonList(name);
- }
+ public static List nameHistoryFromUuid(UUID uuid) throws IOException {
+ String name = getName(uuid);
+ if (name == null) return Collections.emptyList();
+ return Collections.singletonList(name);
+ }
- public static String getName(UUID uuid) throws IOException {
- String url = "https://playerdb.co/api/player/minecraft/" + uuid.toString();
- Request request = new Request.Builder()
- .addHeader("User-Agent", "RedisBungee-ProxioDev")
- .url(url)
- .get()
- .build();
- ResponseBody body = httpClient.newCall(request).execute().body();
- String response = body.string();
- body.close();
+ public static String getName(UUID uuid) throws IOException {
+ String url = "https://playerdb.co/api/player/minecraft/" + uuid.toString();
+ Request request = new Request.Builder()
+ .addHeader("User-Agent", "RedisBungee-ProxioDev")
+ .url(url)
+ .get()
+ .build();
+ ResponseBody body = httpClient.newCall(request).execute().body();
+ String response = body.string();
+ body.close();
- JsonObject json = gson.fromJson(response, JsonObject.class);
- if (!json.has("success") || !json.get("success").getAsBoolean()) return null;
- if (!json.has("data")) return null;
- JsonObject data = json.getAsJsonObject("data");
- if (!data.has("player")) return null;
- JsonObject player = data.getAsJsonObject("player");
- if (!player.has("username")) return null;
+ JsonObject json = gson.fromJson(response, JsonObject.class);
+ if (!json.has("success") || !json.get("success").getAsBoolean()) return null;
+ if (!json.has("data")) return null;
+ JsonObject data = json.getAsJsonObject("data");
+ if (!data.has("player")) return null;
+ JsonObject player = data.getAsJsonObject("player");
+ if (!player.has("username")) return null;
- return player.get("username").getAsString();
- }
+ return player.get("username").getAsString();
+ }
}
\ No newline at end of file
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/UUIDTranslator.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/UUIDTranslator.java
index 7453074..acccf40 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/UUIDTranslator.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/util/uuid/UUIDTranslator.java
@@ -14,13 +14,15 @@ import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException;
-import java.util.*;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
diff --git a/RedisBungee-API/src/main/resources/messages.yml b/RedisBungee-API/src/main/resources/messages.yml
index a1b1853..737b52c 100644
--- a/RedisBungee-API/src/main/resources/messages.yml
+++ b/RedisBungee-API/src/main/resources/messages.yml
@@ -1,2 +1,3 @@
logged-in-other-location: "§cYou logged in from another location!"
-already-logged-in: "§cYou are already logged in!"
\ No newline at end of file
+already-logged-in: "§cYou are already logged in!"
+error: "§cError has occurred"
\ No newline at end of file
diff --git a/RedisBungee-Bungee/build.gradle.kts b/RedisBungee-Bungee/build.gradle.kts
index 78cf66f..07e1cdb 100644
--- a/RedisBungee-Bungee/build.gradle.kts
+++ b/RedisBungee-Bungee/build.gradle.kts
@@ -40,11 +40,11 @@ tasks {
options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
}
runWaterfall {
- waterfallVersion("1.19")
+ waterfallVersion("1.20")
}
compileJava {
options.encoding = Charsets.UTF_8.name()
- options.release.set(8)
+ options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
@@ -73,6 +73,7 @@ tasks {
relocate("com.google.gson", "com.imaginarycode.minecraft.redisbungee.internal.com.google.gson")
relocate("com.google.j2objc", "com.imaginarycode.minecraft.redisbungee.internal.com.google.j2objc")
relocate("com.google.thirdparty", "com.imaginarycode.minecraft.redisbungee.internal.com.google.thirdparty")
+ relocate("com.github.benmanes.caffeine", "com.imaginarycode.minecraft.redisbungee.internal.caffeine")
}
}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeDataManager.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeDataManager.java
deleted file mode 100644
index dea9185..0000000
--- a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeDataManager.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee;
-
-import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
-import com.imaginarycode.minecraft.redisbungee.api.AbstractDataManager;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import net.md_5.bungee.api.chat.BaseComponent;
-import net.md_5.bungee.api.chat.TextComponent;
-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 java.util.UUID;
-
-public class BungeeDataManager extends AbstractDataManager implements Listener {
-
- public BungeeDataManager(RedisBungeePlugin plugin) {
- super(plugin);
- }
-
- @Override
- @EventHandler
- public void onPostLogin(PostLoginEvent event) {
- invalidate(event.getPlayer().getUniqueId());
- }
-
- @Override
- @EventHandler
- public void onPlayerDisconnect(PlayerDisconnectEvent event) {
- invalidate(event.getPlayer().getUniqueId());
- }
-
- @Override
- @EventHandler
- public void onPubSubMessage(PubSubMessageEvent event) {
- handlePubSubMessage(event.getChannel(), event.getMessage());
- }
-
- @Override
- public boolean handleKick(UUID target, String message) {
- // check if the player is online on this proxy
- ProxiedPlayer player = plugin.getPlayer(target);
- if (player == null) return false;
- player.disconnect(TextComponent.fromLegacyText(message));
- return true;
- }
-}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerDataManager.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerDataManager.java
new file mode 100644
index 0000000..e312b7c
--- /dev/null
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerDataManager.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee;
+
+import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
+import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.chat.TextComponent;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.LoginEvent;
+import net.md_5.bungee.api.event.PlayerDisconnectEvent;
+import net.md_5.bungee.api.event.PostLoginEvent;
+import net.md_5.bungee.api.event.ServerConnectedEvent;
+import net.md_5.bungee.api.plugin.Listener;
+import net.md_5.bungee.api.plugin.Plugin;
+import net.md_5.bungee.event.EventHandler;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+
+public class BungeePlayerDataManager extends PlayerDataManager implements Listener {
+ public BungeePlayerDataManager(RedisBungeePlugin plugin) {
+ super(plugin);
+ }
+
+ @Override
+ @EventHandler
+ public void onPlayerChangedServerNetworkEvent(PlayerChangedServerNetworkEvent event) {
+ super.handleNetworkPlayerServerChange(event);
+ }
+
+ @Override
+ @EventHandler
+ public void onNetworkPlayerQuit(PlayerLeftNetworkEvent event) {
+ super.handleNetworkPlayerQuit(event);
+ }
+
+ @Override
+ @EventHandler
+ public void onPubSubMessageEvent(PubSubMessageEvent event) {
+ super.handlePubSubMessageEvent(event);
+ }
+
+ @Override
+ @EventHandler
+ public void onServerConnectedEvent(ServerConnectedEvent event) {
+ final String currentServer = event.getServer().getInfo().getName();
+ final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
+ super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
+ }
+
+ @EventHandler
+ public void onLoginEvent(LoginEvent event) {
+ event.registerIntent((Plugin) plugin);
+ // check if online
+ if (getLastOnline(event.getConnection().getUniqueId()) == 0) {
+ if (!plugin.configuration().restoreOldKickBehavior()) {
+ kickPlayer(event.getConnection().getUniqueId(), plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
+ // wait 3 seconds before releasing the event
+ plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
+ } else {
+ event.setCancelled(true);
+ event.setCancelReason(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN)))));
+ event.completeIntent((Plugin) plugin);
+ }
+ } else {
+ event.completeIntent((Plugin) plugin);
+ }
+
+ }
+
+ @Override
+ @EventHandler
+ public void onLoginEvent(PostLoginEvent event) {
+ super.addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getAddress().getAddress());
+ }
+
+ @Override
+ @EventHandler
+ public void onDisconnectEvent(PlayerDisconnectEvent event) {
+ super.removePlayer(event.getPlayer().getUniqueId());
+ }
+
+
+}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerUtils.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerUtils.java
deleted file mode 100644
index f1a46c6..0000000
--- a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerUtils.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee;
-
-import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
-import net.md_5.bungee.api.connection.PendingConnection;
-import net.md_5.bungee.api.connection.ProxiedPlayer;
-import redis.clients.jedis.UnifiedJedis;
-public class BungeePlayerUtils {
-
- public static void createBungeePlayer(ProxiedPlayer player, UnifiedJedis unifiedJedis, boolean fireEvent) {
- String serverName = null;
- if (player.getServer() != null) {
- serverName = player.getServer().getInfo().getName();
- }
- PendingConnection pendingConnection = player.getPendingConnection();
- PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, pendingConnection.getAddress().getAddress(), fireEvent);
- }
-
-}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
index f540ab6..81988c4 100644
--- a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungee.java
@@ -10,131 +10,91 @@
package com.imaginarycode.minecraft.redisbungee;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
-import com.imaginarycode.minecraft.redisbungee.api.tasks.*;
+import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
+import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils;
+import com.imaginarycode.minecraft.redisbungee.api.util.uuid.NameFetcher;
+import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDFetcher;
+import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands;
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 com.imaginarycode.minecraft.redisbungee.api.*;
-import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
-import com.imaginarycode.minecraft.redisbungee.api.util.uuid.NameFetcher;
-import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDFetcher;
-import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import com.squareup.okhttp.Dispatcher;
import com.squareup.okhttp.OkHttpClient;
import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.api.plugin.Plugin;
-import redis.clients.jedis.*;
+import net.md_5.bungee.api.scheduler.ScheduledTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.JedisPool;
-import java.io.*;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
-import java.util.*;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
public class RedisBungee extends Plugin implements RedisBungeePlugin, ConfigLoader {
private static RedisBungeeAPI apiStatic;
-
private AbstractRedisBungeeAPI api;
private RedisBungeeMode redisBungeeMode;
- private PubSubListener psl = null;
+ private ProxyDataManager proxyDataManager;
+ private BungeePlayerDataManager playerDataManager;
+ private ScheduledTask heartbeatTask;
+ private ScheduledTask cleanupTask;
private Summoner> summoner;
private UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
- private BungeeDataManager dataManager;
private OkHttpClient httpClient;
- private volatile List proxiesIds;
- private final AtomicInteger globalPlayerCount = new AtomicInteger();
- private Future> integrityCheck;
- private Future> heartbeatTask;
- private static final Object SERVER_TO_PLAYERS_KEY = new Object();
- private final Cache> serverToPlayersCache = CacheBuilder.newBuilder()
- .expireAfterWrite(5, TimeUnit.SECONDS)
- .build();
+
+ private final Logger logger = LoggerFactory.getLogger("RedisBungee");
@Override
- public RedisBungeeConfiguration getConfiguration() {
+ public RedisBungeeConfiguration configuration() {
return this.configuration;
}
- @Override
- public int getCount() {
- return this.globalPlayerCount.get();
- }
-
- @Override
- public Set getLocalPlayersAsUuidStrings() {
- ImmutableSet.Builder builder = ImmutableSet.builder();
- for (ProxiedPlayer player : getProxy().getPlayers()) {
- builder.add(player.getUniqueId().toString());
- }
- return builder.build();
- }
-
- @Override
- public AbstractDataManager getDataManager() {
- return this.dataManager;
- }
-
@Override
public AbstractRedisBungeeAPI getAbstractRedisBungeeApi() {
return this.api;
}
+ @Override
+ public ProxyDataManager proxyDataManager() {
+ return this.proxyDataManager;
+ }
+
+ @Override
+ public PlayerDataManager playerDataManager() {
+ return this.playerDataManager;
+ }
+
@Override
public UUIDTranslator getUuidTranslator() {
return this.uuidTranslator;
}
- @Override
- public Multimap serverToPlayersCache() {
- try {
- return this.serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, this::serversToPlayers);
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public List getProxiesIds() {
- return proxiesIds;
- }
-
- @Override
- public PubSubListener getPubSubListener() {
- return this.psl;
- }
-
- @Override
- public void executeAsync(Runnable runnable) {
- this.getProxy().getScheduler().runAsync(this, runnable);
- }
-
- @Override
- public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
- this.getProxy().getScheduler().schedule(this, runnable, time, timeUnit);
- }
-
@Override
public void fireEvent(Object event) {
this.getProxy().getPluginManager().callEvent((Event) event);
@@ -147,17 +107,32 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin getLocalOnlineUUIDs() {
+ HashSet uuids = new HashSet<>();
+ ProxyServer.getInstance().getPlayers().forEach((proxiedPlayer) -> uuids.add(proxiedPlayer.getUniqueId()));
+ return uuids;
+ }
+
+ @Override
+ protected void handlePlatformCommandExecution(String command) {
+ logInfo("Dispatching {}", command);
+ ProxyServer.getInstance().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
+ }
+ };
+ this.playerDataManager = new BungeePlayerDataManager(this);
+
+ getProxy().getPluginManager().registerListener(this, this.playerDataManager);
+ getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this));
+ // start listening
+ getProxy().getScheduler().runAsync(this, proxyDataManager);
+ // heartbeat
+ this.heartbeatTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.publishHeartbeat(), 0, 1, TimeUnit.SECONDS);
+ // cleanup
+ this.cleanupTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.correctionTask(), 0, 60, TimeUnit.SECONDS);
// init the http lib
httpClient = new OkHttpClient();
Dispatcher dispatcher = new Dispatcher(getExecutorService());
@@ -226,29 +232,7 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin implements Listener {
-
-
- public RedisBungeeBungeeListener(RedisBungeePlugin> plugin, List exemptAddresses) {
- super(plugin, exemptAddresses);
- }
-
- @Override
- @EventHandler(priority = HIGHEST)
- public void onLogin(LoginEvent event) {
- event.registerIntent((Plugin) plugin);
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- if (event.isCancelled()) {
- return null;
- }
- if (plugin.getConfiguration().restoreOldKickBehavior()) {
- for (String s : plugin.getProxiesIds()) {
- if (unifiedJedis.sismember("proxy:" + s + ":usersOnline", event.getConnection().getUniqueId().toString())) {
- event.setCancelled(true);
- event.setCancelReason(plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN));
- return null;
- }
- }
- } else if (api.isPlayerOnline(event.getConnection().getUniqueId())) {
- PlayerUtils.setKickedOtherLocation(event.getConnection().getUniqueId().toString(), unifiedJedis);
- api.kickPlayer(event.getConnection().getUniqueId(), plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
- }
- return null;
- } finally {
- event.completeIntent((Plugin) plugin);
- }
- }
- });
- }
-
- @Override
- @EventHandler
- public void onPostLogin(PostLoginEvent event) {
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), unifiedJedis);
- BungeePlayerUtils.createBungeePlayer(event.getPlayer(), unifiedJedis, true);
- return null;
- }
- });
- }
-
- @Override
- @EventHandler
- public void onPlayerDisconnect(PlayerDisconnectEvent event) {
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- PlayerUtils.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), unifiedJedis, true);
- return null;
- }
- });
-
- }
-
- @Override
- @EventHandler
- public void onServerChange(ServerConnectedEvent event) {
- final String currentServer = event.getServer().getInfo().getName();
- final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- unifiedJedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName());
- PayloadUtils.playerServerChangePayload(event.getPlayer().getUniqueId(), unifiedJedis, currentServer, oldServer);
- return null;
- }
- });
- }
-
- @Override
- @EventHandler
- public void onPing(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());
- }
-
- @Override
- @SuppressWarnings("UnstableApiUsage")
- @EventHandler
- public void onPluginMessage(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.executeAsync(() -> {
- ByteArrayDataInput in = ByteStreams.newDataInput(data);
-
- String subchannel = in.readUTF();
- ByteArrayDataOutput out = ByteStreams.newDataOutput();
- String type;
-
- switch (subchannel) {
- case "PlayerList":
- out.writeUTF("PlayerList");
- Set original = Collections.emptySet();
- type = in.readUTF();
- if (type.equals("ALL")) {
- out.writeUTF("ALL");
- original = plugin.getPlayers();
- } else {
- out.writeUTF(type);
- try {
- original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
- } catch (IllegalArgumentException ignored) {
- }
- }
- Set players = new HashSet<>();
- for (UUID uuid : original)
- players.add(plugin.getUuidTranslator().getNameFromUuid(uuid, false));
- out.writeUTF(Joiner.on(',').join(players));
- break;
- case "PlayerCount":
- out.writeUTF("PlayerCount");
- type = in.readUTF();
- if (type.equals("ALL")) {
- out.writeUTF("ALL");
- out.writeInt(plugin.getCount());
- } else {
- out.writeUTF(type);
- try {
- out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
- } catch (IllegalArgumentException e) {
- out.writeInt(0);
- }
- }
- break;
- case "LastOnline":
- String user = in.readUTF();
- out.writeUTF("LastOnline");
- out.writeUTF(user);
- out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
- break;
- case "ServerPlayers":
- String type1 = in.readUTF();
- out.writeUTF("ServerPlayers");
- Multimap multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
-
- boolean includesUsers;
-
- switch (type1) {
- case "COUNT":
- includesUsers = false;
- break;
- case "PLAYERS":
- includesUsers = true;
- break;
- default:
- // TODO: Should I raise an error?
- return;
- }
-
- out.writeUTF(type1);
-
- if (includesUsers) {
- Multimap human = HashMultimap.create();
- for (Map.Entry entry : multimap.entries()) {
- human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
- }
- serializeMultimap(human, true, out);
- } else {
- serializeMultiset(multimap.keys(), out);
- }
- break;
- case "Proxy":
- out.writeUTF("Proxy");
- out.writeUTF(plugin.getConfiguration().getProxyId());
- break;
- case "PlayerProxy":
- String username = in.readUTF();
- out.writeUTF("PlayerProxy");
- out.writeUTF(username);
- out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
- break;
- default:
- return;
- }
-
- ((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
- });
- }
- }
-
- @Override
- @EventHandler
- public void onPubSubMessage(PubSubMessageEvent event) {
- if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + plugin.getAbstractRedisBungeeApi().getProxyId())) {
- String message = event.getMessage();
- if (message.startsWith("/"))
- message = message.substring(1);
- plugin.logInfo("Invoking command via PubSub: /" + message);
- ((Plugin) plugin).getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), message);
- }
- }
-}
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..6d7b6a7
--- /dev/null
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+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.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import net.md_5.bungee.api.AbstractReconnectHandler;
+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.PluginMessageEvent;
+import net.md_5.bungee.api.event.ProxyPingEvent;
+import net.md_5.bungee.api.plugin.Listener;
+import net.md_5.bungee.event.EventHandler;
+
+import java.util.*;
+
+import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultimap;
+import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultiset;
+
+public class RedisBungeeListener implements Listener {
+
+ private final RedisBungeePlugin plugin;
+
+ public RedisBungeeListener(RedisBungeePlugin plugin) {
+ this.plugin = plugin;
+ }
+
+ @EventHandler
+ public void onPing(ProxyPingEvent event) {
+ if (plugin.configuration().getExemptAddresses().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.proxyDataManager().totalNetworkPlayers());
+ }
+
+ @SuppressWarnings("UnstableApiUsage")
+ @EventHandler
+ public void onPluginMessage(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.executeAsync(() -> {
+ ByteArrayDataInput in = ByteStreams.newDataInput(data);
+
+ String subchannel = in.readUTF();
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ String type;
+
+ switch (subchannel) {
+ case "PlayerList" -> {
+ out.writeUTF("PlayerList");
+ Set original = Collections.emptySet();
+ type = in.readUTF();
+ if (type.equals("ALL")) {
+ out.writeUTF("ALL");
+ original = plugin.proxyDataManager().networkPlayers();
+ } else {
+ out.writeUTF(type);
+ try {
+ original = plugin.getAbstractRedisBungeeApi().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));
+ }
+ case "PlayerCount" -> {
+ out.writeUTF("PlayerCount");
+ type = in.readUTF();
+ if (type.equals("ALL")) {
+ out.writeUTF("ALL");
+ out.writeInt(plugin.proxyDataManager().totalNetworkPlayers());
+ } else {
+ out.writeUTF(type);
+ try {
+ out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
+ } catch (IllegalArgumentException e) {
+ out.writeInt(0);
+ }
+ }
+ }
+ case "LastOnline" -> {
+ String user = in.readUTF();
+ out.writeUTF("LastOnline");
+ out.writeUTF(user);
+ out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
+ }
+ case "ServerPlayers" -> {
+ String type1 = in.readUTF();
+ out.writeUTF("ServerPlayers");
+ Multimap multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
+ boolean includesUsers;
+ switch (type1) {
+ case "COUNT" -> includesUsers = false;
+ case "PLAYERS" -> includesUsers = true;
+ 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);
+ }
+ }
+ case "Proxy" -> {
+ out.writeUTF("Proxy");
+ out.writeUTF(plugin.configuration().getProxyId());
+ }
+ case "PlayerProxy" -> {
+ String username = in.readUTF();
+ out.writeUTF("PlayerProxy");
+ out.writeUTF(username);
+ out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
+ }
+ default -> {
+ return;
+ }
+ }
+
+ ((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
+ });
+ }
+ }
+
+
+}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
index e5a9ea3..e6a7264 100644
--- a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
@@ -13,8 +13,8 @@ package com.imaginarycode.minecraft.redisbungee.commands;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
-import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
+import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
@@ -286,9 +286,10 @@ public class RedisBungeeCommands {
public static class ServerIds extends Command {
private final RedisBungee plugin;
+
public ServerIds(RedisBungee plugin) {
super("serverids", "redisbungee.command.serverids");
- this.plugin =plugin;
+ this.plugin = plugin;
}
@Override
@@ -313,8 +314,8 @@ public class RedisBungeeCommands {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
- String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getProxyId();
- if (!plugin.getProxiesIds().contains(proxy)) {
+ String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
+ if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
return;
}
diff --git a/RedisBungee-Velocity/build.gradle.kts b/RedisBungee-Velocity/build.gradle.kts
index 3f4ea99..c477ecd 100644
--- a/RedisBungee-Velocity/build.gradle.kts
+++ b/RedisBungee-Velocity/build.gradle.kts
@@ -39,14 +39,14 @@ tasks {
"https://jd.papermc.io/velocity/3.0.0/", // velocity api
)
val apiDocs = File(rootProject.projectDir, "RedisBungee-API/build/docs/javadoc")
- options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
+ options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
}
runVelocity {
velocityVersion("3.2.0-SNAPSHOT")
}
compileJava {
options.encoding = Charsets.UTF_8.name()
- options.release.set(11)
+ options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
@@ -61,6 +61,7 @@ tasks {
relocate("com.squareup.okhttp", "com.imaginarycode.minecraft.redisbungee.internal.okhttp")
relocate("okio", "com.imaginarycode.minecraft.redisbungee.internal.okio")
relocate("org.json", "com.imaginarycode.minecraft.redisbungee.internal.json")
+ relocate("com.github.benmanes.caffeine", "com.imaginarycode.minecraft.redisbungee.internal.caffeine")
}
}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java
new file mode 100644
index 0000000..3d7d6cf
--- /dev/null
+++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+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.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.velocitypowered.api.event.PostOrder;
+import com.velocitypowered.api.event.Subscribe;
+import com.velocitypowered.api.event.connection.PluginMessageEvent;
+import com.velocitypowered.api.event.proxy.ProxyPingEvent;
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.ServerConnection;
+import com.velocitypowered.api.proxy.server.ServerPing;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultimap;
+import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultiset;
+
+public class RedisBungeeListener {
+
+ private final RedisBungeePlugin plugin;
+
+ public RedisBungeeListener(RedisBungeePlugin plugin) {
+ this.plugin = plugin;
+ }
+
+ @Subscribe(order = PostOrder.LAST) // some plugins changes it online players so we need to be executed as last
+ public void onPing(ProxyPingEvent event) {
+ if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getRemoteAddress().getAddress())) {
+ return;
+ }
+ ServerPing.Builder ping = event.getPing().asBuilder();
+ ping.onlinePlayers(plugin.proxyDataManager().totalNetworkPlayers());
+ event.setPing(ping.build());
+ }
+
+ @Subscribe
+ public void onPluginMessage(PluginMessageEvent event) {
+ if (!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
+ return;
+ }
+
+ event.setResult(PluginMessageEvent.ForwardResult.handled());
+
+ plugin.executeAsync(() -> {
+ ByteArrayDataInput in = event.dataAsDataStream();
+
+ String subchannel = in.readUTF();
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ String type;
+
+ switch (subchannel) {
+ case "PlayerList":
+ out.writeUTF("PlayerList");
+ Set original = Collections.emptySet();
+ type = in.readUTF();
+ if (type.equals("ALL")) {
+ out.writeUTF("ALL");
+ original = plugin.proxyDataManager().networkPlayers();
+ } else {
+ out.writeUTF(type);
+ try {
+ original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
+ } catch (IllegalArgumentException ignored) {
+ }
+ }
+ Set players = original.stream()
+ .map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
+ .collect(Collectors.toSet());
+ out.writeUTF(Joiner.on(',').join(players));
+ break;
+ case "PlayerCount":
+ out.writeUTF("PlayerCount");
+ type = in.readUTF();
+ if (type.equals("ALL")) {
+ out.writeUTF("ALL");
+ out.writeInt(plugin.proxyDataManager().totalNetworkPlayers());
+ } else {
+ out.writeUTF(type);
+ try {
+ out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
+ } catch (IllegalArgumentException e) {
+ out.writeInt(0);
+ }
+ }
+ break;
+ case "LastOnline":
+ String user = in.readUTF();
+ out.writeUTF("LastOnline");
+ out.writeUTF(user);
+ out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
+ break;
+ case "ServerPlayers":
+ String type1 = in.readUTF();
+ out.writeUTF("ServerPlayers");
+ Multimap multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
+
+ boolean includesUsers;
+
+ switch (type1) {
+ case "COUNT" -> includesUsers = false;
+ case "PLAYERS" -> includesUsers = true;
+ default -> {
+ // TODO: Should I raise an error?
+ return;
+ }
+ }
+
+ out.writeUTF(type1);
+
+ if (includesUsers) {
+ Multimap human = HashMultimap.create();
+ for (Map.Entry entry : multimap.entries()) {
+ human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
+ }
+ serializeMultimap(human, true, out);
+ } else {
+ serializeMultiset(multimap.keys(), out);
+ }
+ break;
+ case "Proxy":
+ out.writeUTF("Proxy");
+ out.writeUTF(plugin.configuration().getProxyId());
+ break;
+ case "PlayerProxy":
+ String username = in.readUTF();
+ out.writeUTF("PlayerProxy");
+ out.writeUTF(username);
+ out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
+ break;
+ default:
+ return;
+ }
+
+ ((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
+ });
+
+ }
+
+
+}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java
deleted file mode 100644
index f73bf88..0000000
--- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityListener.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-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.io.ByteArrayDataInput;
-import com.google.common.io.ByteArrayDataOutput;
-import com.google.common.io.ByteStreams;
-import com.imaginarycode.minecraft.redisbungee.api.AbstractRedisBungeeListener;
-import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
-import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
-import com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils;
-import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
-import com.velocitypowered.api.event.Continuation;
-import com.velocitypowered.api.event.PostOrder;
-import com.velocitypowered.api.event.ResultedEvent;
-import com.velocitypowered.api.event.Subscribe;
-import com.velocitypowered.api.event.connection.DisconnectEvent;
-import com.velocitypowered.api.event.connection.LoginEvent;
-import com.velocitypowered.api.event.connection.PluginMessageEvent;
-import com.velocitypowered.api.event.connection.PostLoginEvent;
-import com.velocitypowered.api.event.connection.PluginMessageEvent.ForwardResult;
-import com.velocitypowered.api.event.player.ServerConnectedEvent;
-import com.velocitypowered.api.event.proxy.ProxyPingEvent;
-import com.velocitypowered.api.proxy.Player;
-import com.velocitypowered.api.proxy.ServerConnection;
-import com.velocitypowered.api.proxy.server.ServerPing;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.net.InetAddress;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultimap;
-import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultiset;
-
-public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener {
- // Some messages are using legacy characters
- private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
-
- public RedisBungeeVelocityListener(RedisBungeePlugin> plugin, List exemptAddresses) {
- super(plugin, exemptAddresses);
- }
-
- @Subscribe(order = PostOrder.LAST)
- public void onLogin(LoginEvent event, Continuation continuation) {
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- try {
- if (!event.getResult().isAllowed()) {
- return null;
- }
- if (plugin.getConfiguration().restoreOldKickBehavior()) {
-
- for (String s : plugin.getProxiesIds()) {
- if (unifiedJedis.sismember("proxy:" + s + ":usersOnline", event.getPlayer().getUniqueId().toString())) {
- event.setResult(ResultedEvent.ComponentResult.denied(serializer.deserialize(plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN))));
- return null;
- }
- }
-
- } else if (api.isPlayerOnline(event.getPlayer().getUniqueId())) {
- PlayerUtils.setKickedOtherLocation(event.getPlayer().getUniqueId().toString(), unifiedJedis);
- api.kickPlayer(event.getPlayer().getUniqueId(), plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
- }
- return null;
- } finally {
- continuation.resume();
- }
- }
-
- });
- }
-
- @Override
- @Subscribe
- public void onPostLogin(PostLoginEvent event) {
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- plugin.getUuidTranslator().persistInfo(event.getPlayer().getUsername(), event.getPlayer().getUniqueId(), unifiedJedis);
- VelocityPlayerUtils.createVelocityPlayer(event.getPlayer(), unifiedJedis, true);
- return null;
- }
- });
- }
-
- @Override
- @Subscribe
- public void onPlayerDisconnect(DisconnectEvent event) {
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- PlayerUtils.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), unifiedJedis, true);
- return null;
- }
-
- });
-
- }
-
- @Override
- @Subscribe
- public void onServerChange(ServerConnectedEvent event) {
- final String currentServer = event.getServer().getServerInfo().getName();
- final String oldServer = event.getPreviousServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null);
- plugin.executeAsync(new RedisTask(plugin) {
- @Override
- public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
- unifiedJedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", currentServer);
- PayloadUtils.playerServerChangePayload(event.getPlayer().getUniqueId(), unifiedJedis, currentServer, oldServer);
- return null;
- }
- });
- }
-
- @Override
- @Subscribe(order = PostOrder.LAST) // some plugins changes it online players so we need to be executed as last
- public void onPing(ProxyPingEvent event) {
- if (exemptAddresses.contains(event.getConnection().getRemoteAddress().getAddress())) {
- return;
- }
- ServerPing.Builder ping = event.getPing().asBuilder();
- ping.onlinePlayers(plugin.getCount());
- event.setPing(ping.build());
- }
-
- @Override
- @Subscribe
- public void onPluginMessage(PluginMessageEvent event) {
- if (!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
- return;
- }
-
- event.setResult(ForwardResult.handled());
-
- plugin.executeAsync(() -> {
- ByteArrayDataInput in = event.dataAsDataStream();
-
- String subchannel = in.readUTF();
- ByteArrayDataOutput out = ByteStreams.newDataOutput();
- String type;
-
- switch (subchannel) {
- case "PlayerList":
- out.writeUTF("PlayerList");
- Set original = Collections.emptySet();
- type = in.readUTF();
- if (type.equals("ALL")) {
- out.writeUTF("ALL");
- original = plugin.getPlayers();
- } else {
- out.writeUTF(type);
- try {
- original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
- } catch (IllegalArgumentException ignored) {
- }
- }
- Set players = original.stream()
- .map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
- .collect(Collectors.toSet());
- out.writeUTF(Joiner.on(',').join(players));
- break;
- case "PlayerCount":
- out.writeUTF("PlayerCount");
- type = in.readUTF();
- if (type.equals("ALL")) {
- out.writeUTF("ALL");
- out.writeInt(plugin.getCount());
- } else {
- out.writeUTF(type);
- try {
- out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
- } catch (IllegalArgumentException e) {
- out.writeInt(0);
- }
- }
- break;
- case "LastOnline":
- String user = in.readUTF();
- out.writeUTF("LastOnline");
- out.writeUTF(user);
- out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
- break;
- case "ServerPlayers":
- String type1 = in.readUTF();
- out.writeUTF("ServerPlayers");
- Multimap multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
-
- boolean includesUsers;
-
- switch (type1) {
- case "COUNT":
- includesUsers = false;
- break;
- case "PLAYERS":
- includesUsers = true;
- break;
- default:
- // TODO: Should I raise an error?
- return;
- }
-
- out.writeUTF(type1);
-
- if (includesUsers) {
- Multimap human = HashMultimap.create();
- for (Map.Entry entry : multimap.entries()) {
- human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
- }
- serializeMultimap(human, true, out);
- } else {
- serializeMultiset(multimap.keys(), out);
- }
- break;
- case "Proxy":
- out.writeUTF("Proxy");
- out.writeUTF(plugin.getConfiguration().getProxyId());
- break;
- case "PlayerProxy":
- String username = in.readUTF();
- out.writeUTF("PlayerProxy");
- out.writeUTF(username);
- out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
- break;
- default:
- return;
- }
-
- ((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
- });
-
- }
-
-
- @Override
- @Subscribe
- public void onPubSubMessage(PubSubMessageEvent event) {
- if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + plugin.getAbstractRedisBungeeApi().getProxyId())) {
- String message = event.getMessage();
- if (message.startsWith("/"))
- message = message.substring(1);
- plugin.logInfo("Invoking command via PubSub: /" + message);
- ((RedisBungeeVelocityPlugin) plugin).getProxy().getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), message);
-
- }
- }
-}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityPlugin.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityPlugin.java
index fe0d6c6..db2c870 100644
--- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityPlugin.java
+++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeVelocityPlugin.java
@@ -10,12 +10,11 @@
package com.imaginarycode.minecraft.redisbungee;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
import com.google.inject.Inject;
-import com.imaginarycode.minecraft.redisbungee.api.*;
+import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
@@ -23,7 +22,7 @@ import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEv
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
-import com.imaginarycode.minecraft.redisbungee.api.tasks.*;
+import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.NameFetcher;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDFetcher;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
@@ -46,17 +45,21 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.scheduler.ScheduledTask;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.slf4j.Logger;
-import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisConnectionException;
-
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.InetAddress;
import java.nio.file.Path;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
@Plugin(id = "redisbungee", name = "RedisBungee", version = Constants.VERSION, url = "https://github.com/ProxioDev/RedisBungee", authors = {"astei", "ProxioDev"})
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, ConfigLoader {
@@ -64,29 +67,25 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
private final Logger logger;
private final Path dataFolder;
private final AbstractRedisBungeeAPI api;
- private final PubSubListener psl;
private Summoner> jedisSummoner;
private RedisBungeeMode redisBungeeMode;
private final UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
- private final VelocityDataManager dataManager;
private final OkHttpClient httpClient;
- private volatile List proxiesIds;
- private final AtomicInteger globalPlayerCount = new AtomicInteger();
- private ScheduledTask integrityCheck;
+
+ private final ProxyDataManager proxyDataManager;
+
+ private final VelocityPlayerDataManager playerDataManager;
+
+ private ScheduledTask cleanUpTask;
private ScheduledTask heartbeatTask;
- private static final Object SERVER_TO_PLAYERS_KEY = new Object();
public static final List IDENTIFIERS = List.of(
MinecraftChannelIdentifier.create("legacy", "redisbungee"),
new LegacyChannelIdentifier("RedisBungee"),
// This is needed for clients before 1.13
new LegacyChannelIdentifier("legacy:redisbungee")
);
- private final Cache> serverToPlayersCache = CacheBuilder.newBuilder()
- .expireAfterWrite(5, TimeUnit.SECONDS)
- .build();
-
@Inject
public RedisBungeeVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
@@ -102,11 +101,21 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
}
this.api = new RedisBungeeAPI(this);
InitialUtils.checkRedisVersion(this);
- // check if this proxy is recovering from a crash and start heart the beat.
- InitialUtils.checkIfRecovering(this, getDataFolder());
+ this.proxyDataManager = new ProxyDataManager(this) {
+ @Override
+ public Set getLocalOnlineUUIDs() {
+ HashSet players = new HashSet<>();
+ server.getAllPlayers().forEach(player -> players.add(player.getUniqueId()));
+ return players;
+ }
+
+ @Override
+ protected void handlePlatformCommandExecution(String command) {
+ server.getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), command);
+ }
+ };
+ this.playerDataManager = new VelocityPlayerDataManager(this);
uuidTranslator = new UUIDTranslator(this);
- dataManager = new VelocityDataManager(this);
- psl = new PubSubListener(this);
this.httpClient = new OkHttpClient();
Dispatcher dispatcher = new Dispatcher(Executors.newFixedThreadPool(6));
this.httpClient.setDispatcher(dispatcher);
@@ -115,31 +124,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
}
- @Override
- public RedisBungeeConfiguration getConfiguration() {
- return this.configuration;
- }
-
- @Override
- public int getCount() {
- return this.globalPlayerCount.get();
- }
-
-
- @Override
- public Set getLocalPlayersAsUuidStrings() {
- ImmutableSet.Builder builder = ImmutableSet.builder();
- for (Player player : getProxy().getAllPlayers()) {
- builder.add(player.getUniqueId().toString());
- }
- return builder.build();
- }
-
- @Override
- public AbstractDataManager getDataManager() {
- return this.dataManager;
- }
-
@Override
public Summoner> getSummoner() {
return this.jedisSummoner;
@@ -150,29 +134,21 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
return this.api;
}
+ @Override
+ public ProxyDataManager proxyDataManager() {
+ return this.proxyDataManager;
+ }
+
+ @Override
+ public PlayerDataManager playerDataManager() {
+ return this.playerDataManager;
+ }
+
@Override
public UUIDTranslator getUuidTranslator() {
return this.uuidTranslator;
}
- @Override
- public Multimap serverToPlayersCache() {
- try {
- return this.serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, this::serversToPlayers);
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public List getProxiesIds() {
- return proxiesIds;
- }
-
- @Override
- public PubSubListener getPubSubListener() {
- return this.psl;
- }
@Override
public void executeAsync(Runnable runnable) {
@@ -199,16 +175,36 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
this.getLogger().info(msg);
}
+ @Override
+ public void logInfo(String format, Object... object) {
+ logger.info(format, object);
+ }
+
@Override
public void logWarn(String msg) {
this.getLogger().warn(msg);
}
+ @Override
+ public void logWarn(String format, Object... object) {
+ logger.warn(format, object);
+ }
+
@Override
public void logFatal(String msg) {
this.getLogger().error(msg);
}
+ @Override
+ public void logFatal(String format, Throwable throwable) {
+ logger.error(format, throwable);
+ }
+
+ @Override
+ public RedisBungeeConfiguration configuration() {
+ return this.configuration;
+ }
+
@Override
public Player getPlayer(UUID uuid) {
return this.getProxy().getPlayer(uuid).orElse(null);
@@ -229,6 +225,16 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
return this.getProxy().getPlayer(player).map(Player::getUsername).orElse(null);
}
+ private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
+
+ @Override
+ public boolean handlePlatformKick(UUID uuid, String message) {
+ Player player = getPlayer(uuid);
+ if (player == null) return false;
+ player.disconnect(serializer.deserialize(message));
+ return true;
+ }
+
@Override
public String getPlayerServerName(Player player) {
return player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null);
@@ -247,25 +253,16 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
@Override
public void initialize() {
logInfo("Initializing RedisBungee.....");
- updateProxiesIds();
// start heartbeat task
- heartbeatTask = getProxy().getScheduler().buildTask(this, new HeartbeatTask(this, this.globalPlayerCount)).repeat(HeartbeatTask.INTERVAL, HeartbeatTask.REPEAT_INTERVAL_TIME_UNIT).schedule();
+ // heartbeat and clean up
+ this.heartbeatTask = server.getScheduler().buildTask(this, this.proxyDataManager::publishHeartbeat).repeat(Duration.ofSeconds(1)).schedule();
+ this.cleanUpTask = server.getScheduler().buildTask(this, this.proxyDataManager::correctionTask).repeat(Duration.ofSeconds(60)).schedule();
- getProxy().getEventManager().register(this, new RedisBungeeVelocityListener(this, configuration.getExemptAddresses()));
- getProxy().getEventManager().register(this, dataManager);
- getProxy().getScheduler().buildTask(this, psl).schedule();
-
- IntegrityCheckTask integrityCheckTask = new IntegrityCheckTask(this) {
- @Override
- public void handlePlatformPlayer(String player, UnifiedJedis unifiedJedis) {
- Player playerProxied = getProxy().getPlayer(UUID.fromString(player)).orElse(null);
- if (playerProxied == null)
- return; // We'll deal with it later.
- VelocityPlayerUtils.createVelocityPlayer(playerProxied, unifiedJedis, false);
- }
- };
- integrityCheck = getProxy().getScheduler().buildTask(this, integrityCheckTask::execute).repeat(30, TimeUnit.SECONDS).schedule();
+ server.getEventManager().register(this, this.playerDataManager);
+ server.getEventManager().register(this, new RedisBungeeListener(this));
+ // subscribe
+ server.getScheduler().buildTask(this, this.proxyDataManager).schedule();
// register plugin messages
IDENTIFIERS.forEach(getProxy().getChannelRegistrar()::register);
@@ -292,19 +289,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
public void stop() {
logInfo("Turning off redis connections.....");
// Poison the PubSub listener
- if (psl != null) {
- psl.poison();
- }
- if (integrityCheck != null) {
- integrityCheck.cancel();
+ if (cleanUpTask != null) {
+ cleanUpTask.cancel();
}
if (heartbeatTask != null) {
heartbeatTask.cancel();
}
- ShutdownUtils.shutdownCleanup(this);
+
+
try {
+ this.proxyDataManager.close();
this.jedisSummoner.close();
- } catch (IOException e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
@@ -331,10 +327,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin, Con
return this.redisBungeeMode;
}
- @Override
- public void updateProxiesIds() {
- this.proxiesIds = this.getCurrentProxiesIds(false);
- }
@Subscribe(order = PostOrder.FIRST)
public void onProxyInitializeEvent(ProxyInitializeEvent event) {
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityDataManager.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityDataManager.java
deleted file mode 100644
index 4ad9ef3..0000000
--- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityDataManager.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee;
-
-import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
-import com.imaginarycode.minecraft.redisbungee.api.AbstractDataManager;
-import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
-import com.velocitypowered.api.event.Subscribe;
-import com.velocitypowered.api.event.connection.DisconnectEvent;
-import com.velocitypowered.api.event.connection.PostLoginEvent;
-import com.velocitypowered.api.proxy.Player;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.TextComponent;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
-
-import java.util.UUID;
-
-
-public class VelocityDataManager extends AbstractDataManager {
-
- public VelocityDataManager(RedisBungeePlugin plugin) {
- super(plugin);
- }
-
- @Override
- @Subscribe
- public void onPostLogin(PostLoginEvent event) {
- invalidate(event.getPlayer().getUniqueId());
- }
-
- @Override
- @Subscribe
- public void onPlayerDisconnect(DisconnectEvent event) {
- invalidate(event.getPlayer().getUniqueId());
- }
-
- @Override
- @Subscribe
- public void onPubSubMessage(PubSubMessageEvent event) {
- handlePubSubMessage(event.getChannel(), event.getMessage());
- }
-
- private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
- @Override
- public boolean handleKick(UUID target, String message) {
- Player player = plugin.getPlayer(target);
- if (player == null) {
- return false;
- }
- player.disconnect(serializer.deserialize(message));
- return true;
- }
-}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerDataManager.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerDataManager.java
new file mode 100644
index 0000000..a2b6735
--- /dev/null
+++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerDataManager.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013-present RedisBungee contributors
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.imaginarycode.minecraft.redisbungee;
+
+import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
+import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
+import com.velocitypowered.api.event.Continuation;
+import com.velocitypowered.api.event.ResultedEvent;
+import com.velocitypowered.api.event.Subscribe;
+import com.velocitypowered.api.event.connection.DisconnectEvent;
+import com.velocitypowered.api.event.connection.LoginEvent;
+import com.velocitypowered.api.event.connection.PostLoginEvent;
+import com.velocitypowered.api.event.player.ServerConnectedEvent;
+import com.velocitypowered.api.proxy.Player;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+public class VelocityPlayerDataManager extends PlayerDataManager {
+ public VelocityPlayerDataManager(RedisBungeePlugin plugin) {
+ super(plugin);
+ }
+
+ @Override
+ @Subscribe
+ public void onPlayerChangedServerNetworkEvent(PlayerChangedServerNetworkEvent event) {
+ handleNetworkPlayerServerChange(event);
+ }
+
+ @Override
+ @Subscribe
+ public void onNetworkPlayerQuit(PlayerLeftNetworkEvent event) {
+ handleNetworkPlayerQuit(event);
+ }
+
+ @Override
+ @Subscribe
+ public void onPubSubMessageEvent(PubSubMessageEvent event) {
+ handlePubSubMessageEvent(event);
+ }
+
+ @Override
+ @Subscribe
+ public void onServerConnectedEvent(ServerConnectedEvent event) {
+ final String currentServer = event.getServer().getServerInfo().getName();
+ final String oldServer;
+ if (event.getPreviousServer().isPresent()) {
+ oldServer = event.getPreviousServer().get().getServerInfo().getName();
+ } else {
+ oldServer = null;
+ }
+ super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
+ }
+
+ private static final LegacyComponentSerializer LEGACY_COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().build();
+
+ @Subscribe
+ public void onLoginEvent(LoginEvent event, Continuation continuation) {
+ // check if online
+ if (getLastOnline(event.getPlayer().getUniqueId()) == 0) {
+ if (!plugin.configuration().restoreOldKickBehavior()) {
+ kickPlayer(event.getPlayer().getUniqueId(), plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
+ // wait 3 seconds before releasing the event
+ plugin.executeAsyncAfter(continuation::resume, TimeUnit.SECONDS, 3);
+ } else {
+ event.setResult(ResultedEvent.ComponentResult.denied(LEGACY_COMPONENT_SERIALIZER.deserialize(Objects.requireNonNull(plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN)))));
+ continuation.resume();
+ }
+ } else {
+ continuation.resume();
+ }
+ }
+
+ @Override
+ @Subscribe
+ public void onLoginEvent(PostLoginEvent event) {
+ addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getRemoteAddress().getAddress());
+ }
+
+ @Override
+ @Subscribe
+ public void onDisconnectEvent(DisconnectEvent event) {
+ if (event.getLoginStatus() == DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN || event.getLoginStatus() == DisconnectEvent.LoginStatus.PRE_SERVER_JOIN) {
+ removePlayer(event.getPlayer().getUniqueId());
+ }
+ }
+}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerUtils.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerUtils.java
deleted file mode 100644
index 2f43fc8..0000000
--- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/VelocityPlayerUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2013-present RedisBungee contributors
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- *
- * http://www.eclipse.org/legal/epl-v10.html
- */
-
-package com.imaginarycode.minecraft.redisbungee;
-
-import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
-import com.velocitypowered.api.proxy.Player;
-import com.velocitypowered.api.proxy.ServerConnection;
-import redis.clients.jedis.UnifiedJedis;
-
-import java.util.Optional;
-
-public class VelocityPlayerUtils {
- protected static void createVelocityPlayer(Player player, UnifiedJedis unifiedJedis, boolean fireEvent) {
- Optional optionalServerConnection = player.getCurrentServer();
- String serverName = null;
- if (optionalServerConnection.isPresent()) {
- serverName = optionalServerConnection.get().getServerInfo().getName();
- }
- PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, player.getRemoteAddress().getAddress(), fireEvent);
- }
-
-
-}
diff --git a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
index 3537981..bb9823e 100644
--- a/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
+++ b/RedisBungee-Velocity/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
@@ -10,12 +10,6 @@
package com.imaginarycode.minecraft.redisbungee.commands;
-import java.net.InetAddress;
-import java.text.SimpleDateFormat;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
@@ -25,11 +19,16 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
-
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
+import java.net.InetAddress;
+import java.text.SimpleDateFormat;
+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.
@@ -324,8 +323,8 @@ public class RedisBungeeCommands {
CommandSource sender = invocation.source();
String[] args = invocation.arguments();
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
- String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getProxyId();
- if (!plugin.getProxiesIds().contains(proxy)) {
+ String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
+ if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sender.sendMessage(Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED));
return;
}
diff --git a/gradle.properties b/gradle.properties
index f1c10e0..016d2f9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,2 @@
-group = com.imaginarycode.minecraft
-version = 0.12.0-SNAPSHOT
+group=com.imaginarycode.minecraft
+version=0.12.0-SNAPSHOT
diff --git a/jitpack.yml b/jitpack.yml
index 46c8529..1e41e00 100644
--- a/jitpack.yml
+++ b/jitpack.yml
@@ -1,2 +1,2 @@
jdk:
- - openjdk11
\ No newline at end of file
+ - openjdk17
\ No newline at end of file