getPlayersOnProxy(@NonNull String proxyID) {
+ return plugin.proxyDataManager().getPlayersOn(proxyID);
}
/**
@@ -163,7 +154,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 +165,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 +176,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 +189,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 +213,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.8.0
*/
public final String getProxyId() {
- return plugin.getConfiguration().getProxyId();
+ return plugin.proxyDataManager().proxyId();
}
/**
@@ -245,7 +237,7 @@ public abstract class AbstractRedisBungeeAPI {
* @since 0.8.0
*/
public final List getAllProxies() {
- return plugin.getProxiesIds();
+ return plugin.proxyDataManager().proxiesIds();
}
/**
@@ -266,9 +258,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 +269,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,14 +345,16 @@ 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
+ * @param message kick message that player will see on kick
* @since 0.8.0
+ * @deprecated
*/
-
+ @Deprecated
public void kickPlayer(String playerName, String message) {
- plugin.kickPlayer(playerName, message);
+ kickPlayer(getUuidFromName(playerName), message);
}
/**
@@ -371,11 +363,38 @@ public abstract class AbstractRedisBungeeAPI {
* @param playerUUID player name
* @param message kick message that player will see on kick
* @since 0.8.0
+ * @deprecated
*/
+ @Deprecated
public void kickPlayer(UUID playerUUID, String message) {
- plugin.kickPlayer(playerUUID, message);
+ kickPlayer(playerUUID, Component.text(message));
}
+ /**
+ * 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
+ * @since 0.12.0
+ */
+
+ public void kickPlayer(String playerName, Component message) {
+ kickPlayer(getUuidFromName(playerName), message);
+ }
+
+ /**
+ * Kicks a player from the network
+ *
+ * @param playerUUID player name
+ * @param message kick message that player will see on kick
+ * @since 0.12.0
+ */
+ public void kickPlayer(UUID playerUUID, Component message) {
+ this.plugin.playerDataManager().kickPlayer(playerUUID, message);
+ }
+
+
/**
* This gives you instance of Jedis
*
@@ -457,6 +476,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..a6d600a
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/PlayerDataManager.java
@@ -0,0 +1,271 @@
+/*
+ * 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 net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
+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 lastServerCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis);
+ 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;
+ private final String proxyId;
+ private final String networkId;
+
+ public PlayerDataManager(RedisBungeePlugin plugin) {
+ this.plugin = plugin;
+ this.unifiedJedis = plugin.proxyDataManager().unifiedJedis();
+ this.proxyId = plugin.proxyDataManager().proxyId();
+ this.networkId = plugin.proxyDataManager().networkId();
+ }
+
+ // 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());
+ this.lastServerCache.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(this.proxyId)) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ String message = data.getString("message");
+ plugin.handlePlatformKick(uuid, COMPONENT_SERIALIZER.deserialize(message));
+ return;
+ }
+ if (event.getChannel().equals("redisbungee-serverchange")) {
+ JSONObject data = new JSONObject(event.getMessage());
+ String proxy = data.getString("proxy");
+ if (proxy.equals(this.proxyId)) {
+ return;
+ }
+ UUID uuid = UUID.fromString(data.getString("uuid"));
+ String from = null;
+ if (data.has("from")) 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(this.proxyId)) {
+ 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(this.proxyId)) {
+ 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", this.proxyId);
+ 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);
+ }
+
+ private final JSONComponentSerializer COMPONENT_SERIALIZER =JSONComponentSerializer.json();
+
+ public void kickPlayer(UUID uuid, Component message) {
+ if (!plugin.handlePlatformKick(uuid, message)) { // handle locally before SENDING a message
+ JSONObject data = new JSONObject();
+ data.put("proxy", this.proxyId);
+ data.put("uuid", uuid);
+ data.put("message", COMPONENT_SERIALIZER.serialize(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::" + this.networkId + "::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", this.proxyId);
+ redisData.put("ip", inetAddress.getHostAddress());
+ unifiedJedis.hset("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", redisData);
+
+ JSONObject data = new JSONObject();
+ data.put("proxy", this.proxyId);
+ 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::" + this.networkId + "::player::" + uuid + "::data", "last-online", String.valueOf(System.currentTimeMillis()));
+ unifiedJedis.hdel("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", "server", "proxy", "ip");
+ JSONObject data = new JSONObject();
+ data.put("proxy", this.proxyId);
+ 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::" + this.networkId + "::player::" + uuid + "::data", "proxy");
+ }
+
+ protected String getServerFromRedis(UUID uuid) {
+ return unifiedJedis.hget("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", "server");
+ }
+
+ protected String getLastServerFromRedis(UUID uuid) {
+ return unifiedJedis.hget("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", "last-server");
+ }
+
+ protected InetAddress getIpAddressFromRedis(UUID uuid) {
+ String ip = unifiedJedis.hget("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", "ip");
+ if (ip == null) return null;
+ return InetAddresses.forString(ip);
+ }
+
+ protected long getLastOnlineFromRedis(UUID uuid) {
+ String unixString = unifiedJedis.hget("redis-bungee::" + this.networkId + "::player::" + uuid + "::data", "last-online");
+ if (unixString == null) return -1;
+ return Long.parseLong(unixString);
+ }
+
+ public String getLastServerFor(UUID uuid) {
+ return this.lastServerCache.get(uuid);
+ }
+ 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::" + networkId + "::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::" + networkId + "::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..05e608c
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/ProxyDataManager.java
@@ -0,0 +1,399 @@
+/*
+ * 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.ImmutableMap;
+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 com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil;
+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 {
+
+ 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 networkId;
+
+ private final String proxyId;
+
+ private final String STREAM_ID;
+
+ // 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();
+ this.networkId = plugin.configuration().networkId();
+ this.STREAM_ID = "network-" + this.networkId + "-redisbungee-stream";
+ }
+
+ 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::" + networkId + "::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::" + networkId + "::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;
+ }
+
+ public Map eachProxyCount() {
+ ImmutableMap.Builder builder = ImmutableMap.builder();
+ heartbeats.forEach((proxy, data) -> builder.put(proxy, data.players()));
+ return builder.build();
+ }
+
+ // 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::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
+ pipeline.sadd("redisbungee::" + networkId + "::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::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
+ pipeline.sadd("redisbungee::" + networkId + "::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 ~30 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 > RedisUtil.PROXY_TIMEOUT) {
+ deadProxies.add(id);
+ cleanProxy(id);
+ }
+ }
+ try {
+ new RedisPipelineTask(plugin) {
+ @Override
+ public Void doPooledPipeline(Pipeline pipeline) {
+ for (String deadProxy : deadProxies) {
+ pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
+ }
+ pipeline.sync();
+ return null;
+ }
+
+ @Override
+ public Void clusterPipeline(ClusterPipeline pipeline) {
+ for (String deadProxy : deadProxies) {
+ pipeline.del("redisbungee::" + networkId + "::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));
+ this.heartbeats.remove(id);
+ 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::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString());
+ }
+
+ public void removePlayer(UUID uuid) {
+ this.unifiedJedis.srem("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString());
+ }
+
+ private void destroyProxyMembers() {
+ unifiedJedis.del("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players");
+ }
+
+ private Set getProxyMembers(String proxyId) {
+ Set uuidsStrings = unifiedJedis.smembers("redisbungee::" + this.networkId + "::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);
+ }
+ continue;
+ }
+ 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) {
+ }
+ }
+ }
+ }
+
+ public void close() {
+ 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;
+ }
+
+ public String networkId() {
+ return networkId;
+ }
+}
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..8dc6887 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,18 @@
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.LangConfiguration;
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 net.kyori.adventure.text.Component;
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 +41,56 @@ public interface RedisBungeePlugin extends EventsPlatform {
}
- 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();
- }
-
- AbstractRedisBungeeAPI getAbstractRedisBungeeApi();
-
- UUIDTranslator getUuidTranslator();
-
- Multimap serverToPlayersCache();
-
- 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();
- }
-
- 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();
- }
-
- default void sendProxyCommand(String proxyId, String command) {
- checkArgument(getProxiesIds().contains(proxyId) || proxyId.equals("allservers"), "proxyId is invalid");
- sendChannelMessage("redisbungee-" + proxyId, command);
- }
-
- 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();
- }
-
- PubSubListener getPubSubListener();
-
- 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();
- }
-
- void executeAsync(Runnable runnable);
-
- void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
-
- boolean isOnlineMode();
-
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();
+
+ LangConfiguration langConfiguration();
+
+ Summoner> getSummoner();
+
+ RedisBungeeMode getRedisBungeeMode();
+
+ AbstractRedisBungeeAPI getAbstractRedisBungeeApi();
+
+ ProxyDataManager proxyDataManager();
+
+ PlayerDataManager playerDataManager();
+
+ UUIDTranslator getUuidTranslator();
+
+ boolean isOnlineMode();
+
P getPlayer(UUID uuid);
P getPlayer(String name);
UUID getPlayerUUID(String player);
+
String getPlayerName(UUID player);
+ boolean handlePlatformKick(UUID uuid, Component message);
+
String getPlayerServerName(P player);
boolean isPlayerOnAServer(P player);
InetAddress getPlayerIp(P player);
- default void sendProxyCommand(String cmd) {
- sendProxyCommand(getConfiguration().getProxyId(), cmd);
- }
+ void executeAsync(Runnable runnable);
- 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));
- }
+ void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
- 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/LangConfiguration.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/LangConfiguration.java
new file mode 100644
index 0000000..87aaa11
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/LangConfiguration.java
@@ -0,0 +1,155 @@
+/*
+ * 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.config;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * This language support implementation is temporarily
+ * until I come up with better system but for now we will use Maps instead :/
+ * Todo: possible usage of adventure api
+ */
+public class LangConfiguration {
+
+ private interface RegistrableMessages {
+
+ void register(String id, Locale locale, String miniMessage);
+
+ void test(Locale locale);
+
+ default void throwError(Locale locale, String where) {
+ throw new IllegalStateException("Language system in `" + where + "` found missing entries for " + locale.toString());
+ }
+
+ }
+
+ public static class Messages implements RegistrableMessages{
+
+ private final Map LOGGED_IN_FROM_OTHER_LOCATION;
+ private final Map ALREADY_LOGGED_IN;
+ private final Map SERVER_CONNECTING;
+ private final Map SERVER_NOT_FOUND;
+
+ private final Locale defaultLocale;
+
+ public Messages(Locale defaultLocale) {
+ LOGGED_IN_FROM_OTHER_LOCATION = new HashMap<>();
+ ALREADY_LOGGED_IN = new HashMap<>();
+ SERVER_CONNECTING = new HashMap<>();
+ SERVER_NOT_FOUND = new HashMap<>();
+ this.defaultLocale = defaultLocale;
+ }
+
+ public void register(String id, Locale locale, String miniMessage) {
+ switch (id) {
+ case "server-not-found" -> SERVER_NOT_FOUND.put(locale, miniMessage);
+ case "server-connecting" -> SERVER_CONNECTING.put(locale, miniMessage);
+ case "logged-in-other-location" -> LOGGED_IN_FROM_OTHER_LOCATION.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
+ case "already-logged-in" -> ALREADY_LOGGED_IN.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
+ }
+ }
+
+ public Component alreadyLoggedIn(Locale locale) {
+ if (ALREADY_LOGGED_IN.containsKey(locale)) return ALREADY_LOGGED_IN.get(locale);
+ return ALREADY_LOGGED_IN.get(defaultLocale);
+ }
+
+ // there is no way to know whats client locale during login so just default to use default locale MESSAGES.
+ public Component alreadyLoggedIn() {
+ return this.alreadyLoggedIn(this.defaultLocale);
+ }
+
+ public Component loggedInFromOtherLocation(Locale locale) {
+ if (LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale)) return LOGGED_IN_FROM_OTHER_LOCATION.get(locale);
+ return LOGGED_IN_FROM_OTHER_LOCATION.get(defaultLocale);
+ }
+
+ // there is no way to know what's client locale during login so just default to use default locale MESSAGES.
+ public Component loggedInFromOtherLocation() {
+ return this.loggedInFromOtherLocation(this.defaultLocale);
+ }
+
+ public Component serverConnecting(Locale locale, String server) {
+ String miniMessage;
+ if (SERVER_CONNECTING.containsKey(locale)) {
+ miniMessage = SERVER_CONNECTING.get(locale);
+ } else {
+ miniMessage = SERVER_CONNECTING.get(defaultLocale);
+ }
+ return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server));
+ }
+
+ public Component serverConnecting(String server) {
+ return this.serverConnecting(this.defaultLocale, server);
+ }
+
+ public Component serverNotFound(Locale locale, String server) {
+ String miniMessage;
+ if (SERVER_NOT_FOUND.containsKey(locale)) {
+ miniMessage = SERVER_NOT_FOUND.get(locale);
+ } else {
+ miniMessage = SERVER_NOT_FOUND.get(defaultLocale);
+ }
+ return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server));
+ }
+
+ public Component serverNotFound(String server) {
+ return this.serverNotFound(this.defaultLocale, server);
+ }
+
+
+ // tests locale if set CORRECTLY or just throw if not
+ public void test(Locale locale) {
+ if (!(LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale) && ALREADY_LOGGED_IN.containsKey(locale) && SERVER_CONNECTING.containsKey(locale) && SERVER_NOT_FOUND.containsKey(locale))) {
+ throwError(locale, "messages");
+ }
+ }
+
+ }
+
+ private final Component redisBungeePrefix;
+
+ private final Locale defaultLanguage;
+
+ private final boolean useClientLanguage;
+
+ private final Messages messages;
+
+ public LangConfiguration(Component redisBungeePrefix, Locale defaultLanguage, boolean useClientLanguage, Messages messages) {
+ this.redisBungeePrefix = redisBungeePrefix;
+ this.defaultLanguage = defaultLanguage;
+ this.useClientLanguage = useClientLanguage;
+ this.messages = messages;
+ }
+
+ public Component redisBungeePrefix() {
+ return redisBungeePrefix;
+ }
+
+ public Locale defaultLanguage() {
+ return defaultLanguage;
+ }
+
+ public boolean useClientLanguage() {
+ return useClientLanguage;
+ }
+
+ public Messages messages() {
+ return messages;
+ }
+
+}
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..f76fb39 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
@@ -11,42 +11,39 @@
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 javax.annotation.Nullable;
import java.net.InetAddress;
-import java.util.HashMap;
import java.util.List;
public class RedisBungeeConfiguration {
- public enum MessageType {
- LOGGED_IN_OTHER_LOCATION,
- ALREADY_LOGGED_IN
- }
-
- private final ImmutableMap messages;
- public static final int CONFIG_VERSION = 1;
private final String proxyId;
private final List exemptAddresses;
- private final boolean registerLegacyCommands;
- private final boolean overrideBungeeCommands;
+ private final boolean kickWhenOnline;
- private final boolean restoreOldKickBehavior;
+ private final boolean handleReconnectToLastServer;
+ private final boolean handleMotd;
- public RedisBungeeConfiguration(String proxyId, List exemptAddresses, boolean registerLegacyCommands, boolean overrideBungeeCommands, ImmutableMap messages, boolean restoreOldKickBehavior) {
+ private final CommandsConfiguration commandsConfiguration;
+ private final String networkId;
+
+
+ public RedisBungeeConfiguration(String networkId, String proxyId, List exemptAddresses, boolean kickWhenOnline, boolean handleReconnectToLastServer, boolean handleMotd, CommandsConfiguration commandsConfiguration) {
this.proxyId = proxyId;
- this.messages = messages;
ImmutableList.Builder addressBuilder = ImmutableList.builder();
for (String s : exemptAddresses) {
addressBuilder.add(InetAddresses.forString(s));
}
this.exemptAddresses = addressBuilder.build();
- this.registerLegacyCommands = registerLegacyCommands;
- this.overrideBungeeCommands = overrideBungeeCommands;
- this.restoreOldKickBehavior = restoreOldKickBehavior;
+ this.kickWhenOnline = kickWhenOnline;
+ this.handleReconnectToLastServer = handleReconnectToLastServer;
+ this.handleMotd = handleMotd;
+ this.commandsConfiguration = commandsConfiguration;
+ this.networkId = networkId;
}
+
public String getProxyId() {
return proxyId;
}
@@ -55,19 +52,37 @@ public class RedisBungeeConfiguration {
return exemptAddresses;
}
- public boolean doRegisterLegacyCommands() {
- return registerLegacyCommands;
+ public boolean kickWhenOnline() {
+ return kickWhenOnline;
}
- public boolean doOverrideBungeeCommands() {
- return overrideBungeeCommands;
+ public boolean handleMotd() {
+ return this.handleMotd;
}
- public ImmutableMap getMessages() {
- return messages;
+ public boolean handleReconnectToLastServer() {
+ return this.handleReconnectToLastServer;
}
- public boolean restoreOldKickBehavior() {
- return restoreOldKickBehavior;
+ public record CommandsConfiguration(boolean redisbungeeEnabled, boolean redisbungeeLegacyEnabled,
+ @Nullable LegacySubCommandsConfiguration legacySubCommandsConfiguration) {
+
+ }
+
+ public record LegacySubCommandsConfiguration(boolean findEnabled, boolean glistEnabled, boolean ipEnabled,
+ boolean lastseenEnabled, boolean plistEnabled, boolean pproxyEnabled,
+ boolean sendtoallEnabled, boolean serveridEnabled,
+ boolean serveridsEnabled, boolean installFind, boolean installGlist, boolean installIp,
+ boolean installLastseen, boolean installPlist, boolean installPproxy,
+ boolean installSendtoall, boolean installServerid,
+ boolean installServerids) {
+ }
+
+ public CommandsConfiguration commandsConfiguration() {
+ return commandsConfiguration;
+ }
+
+ public String networkId() {
+ return networkId;
}
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/ConfigLoader.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/ConfigLoader.java
similarity index 54%
rename from RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/ConfigLoader.java
rename to RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/ConfigLoader.java
index b92365e..a73b6ef 100644
--- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/ConfigLoader.java
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/ConfigLoader.java
@@ -8,13 +8,13 @@
* http://www.eclipse.org/legal/epl-v10.html
*/
-package com.imaginarycode.minecraft.redisbungee.api.config;
+package com.imaginarycode.minecraft.redisbungee.api.config.loaders;
-import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
@@ -26,35 +26,29 @@ import redis.clients.jedis.*;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import redis.clients.jedis.providers.PooledConnectionProvider;
-import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
import java.util.*;
-public interface ConfigLoader {
+public interface ConfigLoader extends GenericConfigLoader {
- default void loadConfig(RedisBungeePlugin> plugin, File dataFolder) throws IOException {
- loadConfig(plugin, dataFolder.toPath());
- }
+ int CONFIG_VERSION = 2;
default void loadConfig(RedisBungeePlugin> plugin, Path dataFolder) throws IOException {
- Path configFile = createConfigFile(dataFolder);
+ Path configFile = createConfigFile(dataFolder, "config.yml", "config.yml");
final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build();
ConfigurationNode node = yamlConfigurationFileLoader.load();
- if (node.getNode("config-version").getInt(0) != RedisBungeeConfiguration.CONFIG_VERSION) {
- handleOldConfig(dataFolder);
+ if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
+ handleOldConfig(dataFolder, "config.yml", "config.yml");
node = yamlConfigurationFileLoader.load();
}
final boolean useSSL = node.getNode("useSSL").getBoolean(false);
- final boolean overrideBungeeCommands = node.getNode("override-bungee-commands").getBoolean(false);
- final boolean registerLegacyCommands = node.getNode("register-legacy-commands").getBoolean(false);
- final boolean restoreOldKickBehavior = node.getNode("disable-kick-when-online").getBoolean(false);
+ final boolean kickWhenOnline = node.getNode("kick-when-online").getBoolean(true);
String redisPassword = node.getNode("redis-password").getString("");
String redisUsername = node.getNode("redis-username").getString("");
- String proxyId = node.getNode("proxy-id").getString("test-1");
+ String networkId = node.getNode("network-id").getString("main");
+ String proxyId = node.getNode("proxy-id").getString("proxy-1");
+
final int maxConnections = node.getNode("max-redis-connections").getInt(10);
List exemptAddresses;
try {
@@ -71,10 +65,19 @@ public interface ConfigLoader {
if ((redisUsername.isEmpty() || redisUsername.equals("none"))) {
redisUsername = null;
}
-
- if (useSSL) {
- plugin.logInfo("Using ssl");
+ // env var
+ String proxyIdFromEnv = System.getenv("REDISBUNGEE_PROXY_ID");
+ if (proxyIdFromEnv != null) {
+ plugin.logInfo("Overriding current configured proxy id {} and been set to {} by Environment variable REDISBUNGEE_PROXY_ID", proxyId, proxyIdFromEnv);
+ proxyId = proxyIdFromEnv;
}
+
+ String networkIdFromEnv = System.getenv("REDISBUNGEE_NETWORK_ID");
+ if (networkIdFromEnv != null) {
+ plugin.logInfo("Overriding current configured network id {} and been set to {} by Environment variable REDISBUNGEE_NETWORK_ID", networkId, networkIdFromEnv);
+ networkId = networkIdFromEnv;
+ }
+
// Configuration sanity checks.
if (proxyId == null || proxyId.isEmpty()) {
String genId = UUID.randomUUID().toString();
@@ -86,9 +89,62 @@ public interface ConfigLoader {
} else {
plugin.logInfo("Loaded proxy id " + proxyId);
}
- RedisBungeeConfiguration configuration = new RedisBungeeConfiguration(proxyId, exemptAddresses, registerLegacyCommands, overrideBungeeCommands, getMessagesFromPath(createMessagesFile(dataFolder)), restoreOldKickBehavior);
+
+ if (networkId.isEmpty()) {
+ networkId = "main";
+ plugin.logWarn("network id was empty and replaced with 'main'");
+ }
+
+ plugin.logInfo("Loaded network id " + networkId);
+
+
+
+ boolean reconnectToLastServer = node.getNode("reconnect-to-last-server").getBoolean();
+ boolean handleMotd = node.getNode("handle-motd").getBoolean(true);
+ plugin.logInfo("handle reconnect to last server: {}", reconnectToLastServer);
+ plugin.logInfo("handle motd: {}", handleMotd);
+
+
+ // commands
+ boolean redisBungeeEnabled = node.getNode("commands", "redisbungee", "enabled").getBoolean(true);
+ boolean redisBungeeLegacyEnabled =node.getNode("commands", "redisbungee-legacy", "enabled").getBoolean(false);
+
+ boolean glistEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "enabled").getBoolean(false);
+ boolean findEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled").getBoolean(false);
+ boolean lastseenEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled").getBoolean(false);
+ boolean ipEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "enabled").getBoolean(false);
+ boolean pproxyEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "enabled").getBoolean(false);
+ boolean sendToAllEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "enabled").getBoolean(false);
+ boolean serverIdEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "enabled").getBoolean(false);
+ boolean serverIdsEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "enabled").getBoolean(false);
+ boolean pListEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "enabled").getBoolean(false);
+
+ boolean installGlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "install").getBoolean(false);
+ boolean installFind = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install").getBoolean(false);
+ boolean installLastseen = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install").getBoolean(false);
+ boolean installIp = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "install").getBoolean(false);
+ boolean installPproxy = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "install").getBoolean(false);
+ boolean installSendToAll = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "install").getBoolean(false);
+ boolean installServerid = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "install").getBoolean(false);
+ boolean installServerIds = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "install").getBoolean(false);
+ boolean installPlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "install").getBoolean(false);
+
+
+ RedisBungeeConfiguration configuration = new RedisBungeeConfiguration(networkId, proxyId, exemptAddresses, kickWhenOnline, reconnectToLastServer, handleMotd, new RedisBungeeConfiguration.CommandsConfiguration(
+ redisBungeeEnabled, redisBungeeLegacyEnabled,
+ new RedisBungeeConfiguration.LegacySubCommandsConfiguration(
+ findEnabled, glistEnabled, ipEnabled,
+ lastseenEnabled, pListEnabled, pproxyEnabled,
+ sendToAllEnabled, serverIdEnabled, serverIdsEnabled,
+ installFind, installGlist, installIp,
+ installLastseen, installPlist, installPproxy,
+ installSendToAll, installServerid, installServerIds)
+ ));
Summoner> summoner;
RedisBungeeMode redisBungeeMode;
+ if (useSSL) {
+ plugin.logInfo("Using ssl");
+ }
if (node.getNode("cluster-mode-enabled").getBoolean(false)) {
plugin.logInfo("RedisBungee MODE: CLUSTER");
Set hostAndPortSet = new HashSet<>();
@@ -115,7 +171,7 @@ public interface ConfigLoader {
throw new RuntimeException("No redis server specified");
}
JedisPool jedisPool = null;
- if (node.getNode("enable-jedis-pool-compatibility").getBoolean(true)) {
+ if (node.getNode("enable-jedis-pool-compatibility").getBoolean(false)) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(node.getNode("compatibility-max-connections").getInt(3));
config.setBlockWhenExhausted(true);
@@ -134,53 +190,5 @@ public interface ConfigLoader {
void onConfigLoad(RedisBungeeConfiguration configuration, Summoner> summoner, RedisBungeeMode mode);
- default ImmutableMap getMessagesFromPath(Path path) throws IOException {
- final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(path).build();
- ConfigurationNode node = yamlConfigurationFileLoader.load();
- HashMap messages = new HashMap<>();
- messages.put(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION, node.getNode("logged-in-other-location").getString("§cLogged in from another location."));
- messages.put(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN, node.getNode("already-logged-in").getString("§cYou are already logged in!"));
- return ImmutableMap.copyOf(messages);
- }
-
- default Path createMessagesFile(Path dataFolder) throws IOException {
- if (Files.notExists(dataFolder)) {
- Files.createDirectory(dataFolder);
- }
- Path file = dataFolder.resolve("messages.yml");
- if (Files.notExists(file)) {
- try (InputStream in = getClass().getClassLoader().getResourceAsStream("messages.yml")) {
- Files.createFile(file);
- assert in != null;
- Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
- }
- }
- return file;
- }
-
- default Path createConfigFile(Path dataFolder) throws IOException {
- if (Files.notExists(dataFolder)) {
- Files.createDirectory(dataFolder);
- }
- Path file = dataFolder.resolve("config.yml");
- if (Files.notExists(file)) {
- try (InputStream in = getClass().getClassLoader().getResourceAsStream("config.yml")) {
- Files.createFile(file);
- assert in != null;
- Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
- }
- }
- return file;
- }
-
- default void handleOldConfig(Path dataFolder) throws IOException {
- Path oldConfigFolder = dataFolder.resolve("old_config");
- if (Files.notExists(oldConfigFolder)) {
- Files.createDirectory(oldConfigFolder);
- }
- Path oldConfigPath = dataFolder.resolve("config.yml");
- Files.move(oldConfigPath, oldConfigFolder.resolve(UUID.randomUUID() + "_config.yml"));
- createConfigFile(dataFolder);
- }
}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/GenericConfigLoader.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/GenericConfigLoader.java
new file mode 100644
index 0000000..78a5d29
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/GenericConfigLoader.java
@@ -0,0 +1,58 @@
+/*
+ * 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.config.loaders;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.time.Instant;
+
+
+public interface GenericConfigLoader {
+
+ // CHANGES on every reboot
+ String RANDOM_OLD = "backup-" + Instant.now().getEpochSecond();
+
+ default Path createConfigFile(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
+ if (Files.notExists(dataFolder)) {
+ Files.createDirectory(dataFolder);
+ }
+ Path file = dataFolder.resolve(configFile);
+ if (Files.notExists(file) && defaultResourceID != null) {
+ try (InputStream in = getClass().getClassLoader().getResourceAsStream(defaultResourceID)) {
+ Files.createFile(file);
+ assert in != null;
+ Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+ return file;
+ }
+
+ default void handleOldConfig(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
+ Path oldConfigFolder = dataFolder.resolve("old_config");
+ if (Files.notExists(oldConfigFolder)) {
+ Files.createDirectory(oldConfigFolder);
+ }
+ Path randomStoreConfigDirectory = oldConfigFolder.resolve(RANDOM_OLD);
+ if (Files.notExists(randomStoreConfigDirectory)) {
+ Files.createDirectory(randomStoreConfigDirectory);
+ }
+ Path oldConfigPath = dataFolder.resolve(configFile);
+
+ Files.move(oldConfigPath, randomStoreConfigDirectory.resolve(configFile));
+ createConfigFile(dataFolder, configFile, defaultResourceID);
+ }
+
+}
diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/LangConfigLoader.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/LangConfigLoader.java
new file mode 100644
index 0000000..ad30c42
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/config/loaders/LangConfigLoader.java
@@ -0,0 +1,55 @@
+/*
+ * 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.config.loaders;
+
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.config.LangConfiguration;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import ninja.leaping.configurate.ConfigurationNode;
+import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Locale;
+
+public interface LangConfigLoader extends GenericConfigLoader {
+
+ int CONFIG_VERSION = 1;
+
+ default void loadLangConfig(RedisBungeePlugin> plugin, Path dataFolder) throws IOException {
+ Path configFile = createConfigFile(dataFolder, "lang.yml", "lang.yml");
+ final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build();
+ ConfigurationNode node = yamlConfigurationFileLoader.load();
+ if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
+ handleOldConfig(dataFolder, "lang.yml", "lang.yml");
+ node = yamlConfigurationFileLoader.load();
+ }
+ // MINI message serializer
+ MiniMessage miniMessage = MiniMessage.miniMessage();
+
+ Component prefix = miniMessage.deserialize(node.getNode("prefix").getString("[RedisBungee]"));
+ Locale defaultLocale = Locale.forLanguageTag(node.getNode("default-locale").getString("en-us"));
+ boolean useClientLocale = node.getNode("use-client-locale").getBoolean(true);
+ LangConfiguration.Messages messages = new LangConfiguration.Messages(defaultLocale);
+ node.getNode("messages").getChildrenMap().forEach((key, childNode) -> childNode.getChildrenMap().forEach((childKey, childChildNode) -> {
+ messages.register(key.toString(), Locale.forLanguageTag(childKey.toString()), childChildNode.getString());
+ }));
+ messages.test(defaultLocale);
+
+ onLangConfigLoad(new LangConfiguration(prefix, defaultLocale, useClientLocale, messages));
+ }
+
+
+ void onLangConfigLoad(LangConfiguration langConfiguration);
+
+
+}
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/tasks/UUIDCleanupTask.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/UUIDCleanupTask.java
new file mode 100644
index 0000000..6e080c4
--- /dev/null
+++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/UUIDCleanupTask.java
@@ -0,0 +1,56 @@
+/*
+ * 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.google.gson.Gson;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.api.util.uuid.CachedUUIDEntry;
+import redis.clients.jedis.UnifiedJedis;
+import redis.clients.jedis.exceptions.JedisException;
+
+import java.util.ArrayList;
+
+
+public class UUIDCleanupTask extends RedisTask{
+
+ private final Gson gson = new Gson();
+ private final RedisBungeePlugin> plugin;
+
+ public UUIDCleanupTask(RedisBungeePlugin> plugin) {
+ super(plugin);
+ this.plugin = plugin;
+ }
+
+ // this code is inspired from https://github.com/minecrafter/redisbungeeclean
+ @Override
+ public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
+ try {
+ final long number = unifiedJedis.hlen("uuid-cache");
+ plugin.logInfo("Found {} entries", number);
+ ArrayList fieldsToRemove = new ArrayList<>();
+ unifiedJedis.hgetAll("uuid-cache").forEach((field, data) -> {
+ CachedUUIDEntry cachedUUIDEntry = gson.fromJson(data, CachedUUIDEntry.class);
+ if (cachedUUIDEntry.expired()) {
+ fieldsToRemove.add(field);
+ }
+ });
+ if (!fieldsToRemove.isEmpty()) {
+ unifiedJedis.hdel("uuid-cache", fieldsToRemove.toArray(new String[0]));
+ }
+ plugin.logInfo("deleted {} entries", fieldsToRemove.size());
+ } catch (JedisException e) {
+ plugin.logFatal("There was an error fetching information", e);
+ }
+ return null;
+ }
+
+
+}
\ No newline at end of file
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..8ebf8d0
--- /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 " + RedisUtil.MAJOR_VERSION + "." + RedisUtil.MINOR_VERSION + " 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..7e337db 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,10 @@ import com.google.common.annotations.VisibleForTesting;
@VisibleForTesting
public class RedisUtil {
public final static int PROXY_TIMEOUT = 30;
+
+ public static final int MAJOR_VERSION = 6;
+ public static final int MINOR_VERSION = 2;
+
public static boolean isRedisVersionRight(String redisVersion) {
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
@@ -12,7 +16,10 @@ public class RedisUtil {
}
int major = Integer.parseInt(args[0]);
int minor = Integer.parseInt(args[1]);
- return major >= 3 && minor >= 0;
+
+ if (major > MAJOR_VERSION) return true;
+ return major == MAJOR_VERSION && minor >= MINOR_VERSION;
+
}
// Ham1255: i am keeping this if some plugin uses this *IF*
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/config.yml b/RedisBungee-API/src/main/resources/config.yml
index 85ef226..786efd9 100644
--- a/RedisBungee-API/src/main/resources/config.yml
+++ b/RedisBungee-API/src/main/resources/config.yml
@@ -1,7 +1,14 @@
# RedisBungee configuration file.
-# Get Redis from http://redis.io/
+# Notice:
+# Redis 7.2.4 is last free and open source Redis version after license change
+# https://download.redis.io/releases/redis-7.2.4.tar.gz which you have to compile yourself,
+# unless your package manager still provide it.
+# Here is The alternatives
+# - 'ValKey' By linux foundation https://valkey.io/download/
+# - 'KeyDB' by Snapchat inc https://docs.keydb.dev/docs/download/
-# The Redis server you use.
+
+# The 'Redis', 'ValKey', 'KeyDB' server you will use.
# these settings are ignored when cluster mode is enabled.
redis-server: 127.0.0.1
redis-port: 6379
@@ -12,7 +19,7 @@ cluster-mode-enabled: false
# FORMAT:
# redis-cluster-servers:
-# - host: 127.0.0.1
+# - host: 127.0.0.1`
# port: 2020
# - host: 127.0.0.1
# port: 2021
@@ -25,11 +32,10 @@ redis-cluster-servers:
- host: 127.0.0.1
port: 6379
-# THIS FEATURE IS REDIS V6+
# OPTIONAL: if your redis uses acl usernames set the username here. leave empty for no username.
redis-username: ""
-# OPTIONAL but recommended: If your Redis server uses AUTH, set the password required.
+# OPTIONAL but recommended: If your Redis server uses AUTH, set the required password.
redis-password: ""
# Maximum connections that will be maintained to the Redis server.
@@ -37,44 +43,100 @@ redis-password: ""
# inefficient plugins or a lot of players.
max-redis-connections: 10
-# since redis can support ssl by version 6 you can use ssl / tls in redis bungee too!
+# since redis can support ssl by version 6 you can use SSL/TLS in redis bungee too!
# but there is more configuration needed to work see https://github.com/ProxioDev/RedisBungee/issues/18
# Keep note that SSL/TLS connections will decrease redis performance so use it when needed.
useSSL: false
-# An identifier for this BungeeCord / Velocity instance. Will randomly generate if leaving it blank.
-proxy-id: "test-1"
+# An identifier for this network, which helps to separate redisbungee instances on same redis instance.
+# You can use environment variable 'REDISBUNGEE_NETWORK_ID' to override
+network-id: "main"
-# since version 0.8.0 Internally now uses JedisPooled instead of Jedis, JedisPool.
+# An identifier for this BungeeCord / Velocity instance. Will randomly generate if leaving it blank.
+# You can set Environment variable 'REDISBUNGEE_PROXY_ID' to override
+proxy-id: "proxy-1"
+
+# since RedisBungee Internally now uses UnifiedJedis instead of Jedis, JedisPool.
# which will break compatibility with old plugins that uses RedisBungee JedisPool
-# so to mitigate this issue, we will instruct RedisBungee to init an JedisPool for compatibility reasons.
-# enabled by default
-# ignored when cluster mode is enabled
-enable-jedis-pool-compatibility: true
+# so to mitigate this issue, RedisBungee will create an JedisPool for compatibility reasons.
+# disabled by default
+# Automatically disabled when cluster mode is enabled
+enable-jedis-pool-compatibility: false
+
# max connections for the compatibility pool
compatibility-max-connections: 3
-# Register redis bungee legacy commands
-# if this disabled override-bungee-commands will be ignored
-register-legacy-commands: false
+# restore old login behavior before 0.9.0 update
+# enabled by default
+# when true: when player login and there is old player with same uuid it will get disconnected as result and new player will log in
+# when false: when a player login but login will fail because old player is still connected.
+kick-when-online: true
-# Whether or not RedisBungee should install its version of regular BungeeCord commands.
-# Often, the RedisBungee commands are desired, but in some cases someone may wish to
-# override the commands using another plugin.
-#
-# If you are just denying access to the commands, RedisBungee uses the default BungeeCord
-# permissions - just deny them and access will be denied.
-#
-# Please note that with build 787+, most commands overridden by RedisBungee were moved to
-# modules, and these must be disabled or overridden yourself.
-override-bungee-commands: false
+# enabled by default
+# this option tells RedisBungee handle motd and set online count, when motd is requested
+# you can disable this when you want to handle motd yourself, use RedisBungee api to get total players when needed :)
+handle-motd: true
# A list of IP addresses for which RedisBungee will not modify the response for, useful for automatic
# restart scripts.
+# Automatically disabled if handle-motd is disabled.
exempt-ip-addresses: []
-# restore old login when online behavior before 0.9.0 update
-disable-kick-when-online: false
+# disabled by default
+# RedisBungee will attempt to connect player to last server that was stored.
+reconnect-to-last-server: false
+
+# For redis bungee legacy commands
+# either can be run using '/rbl glist' for example
+# or if 'install' is set to true '/glist' can be used.
+# 'install' also overrides the proxy installed commands
+#
+# In legacy commands each command got it own permissions since they had it own permission pre new command system,
+# so it's also applied to subcommands in '/rbl'.
+commands:
+ # Permission redisbungee.legacy.use
+ redisbungee-legacy:
+ enabled: false
+ subcommands:
+ # Permission redisbungee.command.glist
+ glist:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.find
+ find:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.lastseen
+ lastseen:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.ip
+ ip:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.pproxy
+ pproxy:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.sendtoall
+ sendtoall:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.serverid
+ serverid:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.serverids
+ serverids:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.plist
+ plist:
+ enabled: false
+ install: false
+ # Permission redisbungee.command.use
+ redisbungee:
+ enabled: true
# Config version DO NOT CHANGE!!!!
-config-version: 1
+config-version: 2
diff --git a/RedisBungee-API/src/main/resources/lang.yml b/RedisBungee-API/src/main/resources/lang.yml
new file mode 100644
index 0000000..817a053
--- /dev/null
+++ b/RedisBungee-API/src/main/resources/lang.yml
@@ -0,0 +1,55 @@
+# this config file is for messages / Languages
+# use MiniMessage format https://docs.advntr.dev/minimessage/format.html
+# for colors etc... Legacy chat color is not supported.
+
+# Language codes used in minecraft from the minecraft wiki
+# example: en-us for american english and ar-sa for arabic
+
+# all codes can be obtained from link below
+# from the colum Locale Code -> In-game
+# NOTE: minecraft wiki shows languages like this `en_us` in config it should be `en-us`
+# https://minecraft.wiki/w/Language
+
+# example:
+# lets assume we want to add arabic language.
+# messages:
+# logged-in-other-location:
+# en-us: "You logged in from another location!"
+# ar-sa: "لقد اتصلت من مكان اخر"
+
+
+# RedisBungee Prefix if ever used.
+prefix: "[RedisBungee]"
+
+# en-us is american English, Which is the default language used when a language for a message isn't defined.
+# Warning: IF THE set default locale wasn't defined in the config for all messages, plugin will not load.
+# set the Default locale
+default-locale: en-us
+
+# send language based on client sent settings
+# if you don't have languages configured For client Language
+# it will default to language that has been set above
+# NOTE: due minecraft protocol not sending player settings during login,
+# some of the messages like logged-in-other-location will
+# skip translation and use default locale that has been set in default-locale.
+use-client-locale: true
+
+# messages that are used during login, and connecting to Last server
+messages:
+ logged-in-other-location:
+ en-us: "You logged in from another location!"
+ pt-br: "Você está logado em outra localização!"
+ already-logged-in:
+ en-us: "You are already logged in!"
+ pt-br: "Você já está logado!"
+ server-not-found:
+ # placeholder displays server name in the message.
+ en-us: "unable to connect you to the last server, because server was not found."
+ pt-br: "falha ao conectar você ao último servidor, porque o servidor não foi encontrado."
+ server-connecting:
+ # placeholder displays server name in the message.
+ en-us: "Connecting you to ..."
+ pt-br: "Conectando você a ..."
+
+# DO NOT CHANGE!!!!!
+config-version: 1
diff --git a/RedisBungee-API/src/main/resources/messages.yml b/RedisBungee-API/src/main/resources/messages.yml
deleted file mode 100644
index a1b1853..0000000
--- a/RedisBungee-API/src/main/resources/messages.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-logged-in-other-location: "§cYou logged in from another location!"
-already-logged-in: "§cYou are already logged in!"
\ No newline at end of file
diff --git a/RedisBungee-Bungee/build.gradle.kts b/RedisBungee-Bungee/build.gradle.kts
index 78cf66f..217be17 100644
--- a/RedisBungee-Bungee/build.gradle.kts
+++ b/RedisBungee-Bungee/build.gradle.kts
@@ -5,18 +5,17 @@ plugins {
id("xyz.jpenilla.run-waterfall") version "2.0.0"
}
-
-repositories {
- mavenCentral()
- maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // bungeecord
-}
-val bungeecordApiVersion = "1.19-R0.1-SNAPSHOT"
dependencies {
api(project(":RedisBungee-API"))
- compileOnly("net.md-5:bungeecord-api:$bungeecordApiVersion") {
+ compileOnly(libs.platform.bungeecord) {
exclude("com.google.guava", "guava")
exclude("com.google.code.gson", "gson")
+ exclude("net.kyori","adventure-api")
}
+ implementation(libs.adventure.platforms.bungeecord)
+ implementation(libs.adventure.gson)
+ implementation(libs.acf.bungeecord)
+ implementation(project(":RedisBungee-Commands"))
}
description = "RedisBungee Bungeecord implementation"
@@ -40,11 +39,13 @@ tasks {
options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
}
runWaterfall {
- waterfallVersion("1.19")
+ waterfallVersion("1.20")
+ environment["REDISBUNGEE_PROXY_ID"] = "bungeecord-1"
+ environment["REDISBUNGEE_NETWORK_ID"] = "dev"
}
compileJava {
options.encoding = Charsets.UTF_8.name()
- options.release.set(8)
+ options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
@@ -73,6 +74,10 @@ 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")
+ // acf shade
+ relocate("co.aikar.commands", "com.imaginarycode.minecraft.redisbungee.internal.acf.commands")
+
}
}
diff --git a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeCommandPlatformHelper.java b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeCommandPlatformHelper.java
new file mode 100644
index 0000000..019d06f
--- /dev/null
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeeCommandPlatformHelper.java
@@ -0,0 +1,17 @@
+package com.imaginarycode.minecraft.redisbungee;
+
+import co.aikar.commands.BungeeCommandIssuer;
+import co.aikar.commands.CommandIssuer;
+import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
+
+public class BungeeCommandPlatformHelper extends CommandPlatformHelper {
+
+ @Override
+ public void sendMessage(CommandIssuer issuer, Component component) {
+ BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer;
+ bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component));
+ }
+
+}
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..8540005
--- /dev/null
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/BungeePlayerDataManager.java
@@ -0,0 +1,97 @@
+/*
+ * 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.events.PlayerChangedServerNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
+import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
+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.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().kickWhenOnline()) {
+ kickPlayer(event.getConnection().getUniqueId(), plugin.langConfiguration().messages().loggedInFromOtherLocation());
+ // wait 3 seconds before releasing the event
+ plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
+ } else {
+ event.setCancelled(true);
+ event.setCancelReason(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().alreadyLoggedIn()));
+ 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..d5ae22b 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,104 @@
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.config.ConfigLoader;
+import co.aikar.commands.BungeeCommandManager;
+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.LangConfiguration;
+import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
+import com.imaginarycode.minecraft.redisbungee.api.config.loaders.LangConfigLoader;
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.commands.RedisBungeeCommands;
+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.CommandLoader;
+import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
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.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.ProxyServer;
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.sql.Date;
+import java.time.Instant;
+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 {
+public class RedisBungee extends Plugin implements RedisBungeePlugin, ConfigLoader, LangConfigLoader {
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 LangConfiguration langConfiguration;
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 BungeeCommandManager commandManager;
+
+ 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();
+ public LangConfiguration langConfiguration() {
+ return this.langConfiguration;
}
- @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 +120,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,71 +247,49 @@ 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..b5c401f
--- /dev/null
+++ b/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeListener.java
@@ -0,0 +1,168 @@
+/*
+ * 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.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
+import net.md_5.bungee.api.AbstractReconnectHandler;
+import net.md_5.bungee.api.ProxyServer;
+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.event.ServerConnectEvent;
+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.*;
+
+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().handleMotd()) return;
+ 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());
+ });
+ }
+ }
+
+ @EventHandler
+ public void onServerConnectEvent(ServerConnectEvent event) {
+ if (event.getReason() == ServerConnectEvent.Reason.JOIN_PROXY && plugin.configuration().handleReconnectToLastServer()) {
+ ProxiedPlayer player = event.getPlayer();
+ String lastServer = plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
+ if (lastServer == null) return;
+ player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverConnecting(player.getLocale(), lastServer)));
+ ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(lastServer);
+ if (serverInfo == null) {
+ player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverNotFound(player.getLocale(), lastServer)));
+ return;
+ }
+ event.setTarget(serverInfo);
+ }
+ }
+}
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
deleted file mode 100644
index e5a9ea3..0000000
--- a/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/RedisBungeeCommands.java
+++ /dev/null
@@ -1,353 +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.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 net.md_5.bungee.api.ChatColor;
-import net.md_5.bungee.api.CommandSender;
-import net.md_5.bungee.api.chat.BaseComponent;
-import net.md_5.bungee.api.chat.ComponentBuilder;
-import net.md_5.bungee.api.chat.TextComponent;
-import net.md_5.bungee.api.config.ServerInfo;
-import net.md_5.bungee.api.plugin.Command;
-
-import java.net.InetAddress;
-import java.text.SimpleDateFormat;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-
-/**
- * This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
- *
- * All classes use the {@link AbstractRedisBungeeAPI}.
- *
- * @author tuxed
- * @since 0.2.3
- */
-public class RedisBungeeCommands {
- private static final BaseComponent[] NO_PLAYER_SPECIFIED =
- new ComponentBuilder("You must specify a player name.").color(ChatColor.RED).create();
- private static final BaseComponent[] PLAYER_NOT_FOUND =
- new ComponentBuilder("No such player found.").color(ChatColor.RED).create();
- private static final BaseComponent[] NO_COMMAND_SPECIFIED =
- new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
-
- private static String playerPlural(int num) {
- return num == 1 ? num + " player is" : num + " players are";
- }
-
- public static class GlistCommand extends Command {
- private final RedisBungee plugin;
-
- public GlistCommand(RedisBungee plugin) {
- super("glist", "bungeecord.command.list", "redisbungee", "rglist");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- int count = plugin.getAbstractRedisBungeeApi().getPlayerCount();
- BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
- .append(playerPlural(count) + " currently online.").create();
- if (args.length > 0 && args[0].equals("showall")) {
- Multimap serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
- Multimap human = HashMultimap.create();
- for (Map.Entry entry : serverToPlayers.entries()) {
- // if for any reason UUID translation fails just return the uuid as name, to make command finish executing.
- String playerName = plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false);
- human.put(entry.getKey(), playerName != null ? playerName : entry.getValue().toString());
- }
- for (String server : new TreeSet<>(serverToPlayers.keySet())) {
- TextComponent serverName = new TextComponent();
- serverName.setColor(ChatColor.GREEN);
- serverName.setText("[" + server + "] ");
- TextComponent serverCount = new TextComponent();
- serverCount.setColor(ChatColor.YELLOW);
- serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
- TextComponent serverPlayers = new TextComponent();
- serverPlayers.setColor(ChatColor.WHITE);
- serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
- sender.sendMessage(serverName, serverCount, serverPlayers);
- }
- sender.sendMessage(playersOnline);
- } else {
- sender.sendMessage(playersOnline);
- sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
- }
- }
- });
- }
- }
-
- public static class FindCommand extends Command {
- private final RedisBungee plugin;
-
- public FindCommand(RedisBungee plugin) {
- super("find", "bungeecord.command.find", "rfind");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- if (args.length > 0) {
- UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
- if (uuid == null) {
- sender.sendMessage(PLAYER_NOT_FOUND);
- return;
- }
- ServerInfo si = plugin.getProxy().getServerInfo(plugin.getAbstractRedisBungeeApi().getServerNameFor(uuid));
- if (si != null) {
- TextComponent message = new TextComponent();
- message.setColor(ChatColor.BLUE);
- message.setText(args[0] + " is on " + si.getName() + ".");
- sender.sendMessage(message);
- } else {
- sender.sendMessage(PLAYER_NOT_FOUND);
- }
- } else {
- sender.sendMessage(NO_PLAYER_SPECIFIED);
- }
- }
- });
- }
- }
-
- public static class LastSeenCommand extends Command {
- private final RedisBungee plugin;
-
- public LastSeenCommand(RedisBungee plugin) {
- super("lastseen", "redisbungee.command.lastseen", "rlastseen");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- if (args.length > 0) {
- UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
- if (uuid == null) {
- sender.sendMessage(PLAYER_NOT_FOUND);
- return;
- }
- long secs = plugin.getAbstractRedisBungeeApi().getLastOnline(uuid);
- TextComponent message = new TextComponent();
- if (secs == 0) {
- message.setColor(ChatColor.GREEN);
- message.setText(args[0] + " is currently online.");
- } else if (secs != -1) {
- message.setColor(ChatColor.BLUE);
- message.setText(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
- } else {
- message.setColor(ChatColor.RED);
- message.setText(args[0] + " has never been online.");
- }
- sender.sendMessage(message);
- } else {
- sender.sendMessage(NO_PLAYER_SPECIFIED);
- }
- }
- });
- }
- }
-
- public static class IpCommand extends Command {
- private final RedisBungee plugin;
-
- public IpCommand(RedisBungee plugin) {
- super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- if (args.length > 0) {
- UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
- if (uuid == null) {
- sender.sendMessage(PLAYER_NOT_FOUND);
- return;
- }
- InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
- if (ia != null) {
- TextComponent message = new TextComponent();
- message.setColor(ChatColor.GREEN);
- message.setText(args[0] + " is connected from " + ia.toString() + ".");
- sender.sendMessage(message);
- } else {
- sender.sendMessage(PLAYER_NOT_FOUND);
- }
- } else {
- sender.sendMessage(NO_PLAYER_SPECIFIED);
- }
- }
- });
- }
- }
-
- public static class PlayerProxyCommand extends Command {
- private final RedisBungee plugin;
-
- public PlayerProxyCommand(RedisBungee plugin) {
- super("pproxy", "redisbungee.command.pproxy");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- if (args.length > 0) {
- UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
- if (uuid == null) {
- sender.sendMessage(PLAYER_NOT_FOUND);
- return;
- }
- String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
- if (proxy != null) {
- TextComponent message = new TextComponent();
- message.setColor(ChatColor.GREEN);
- message.setText(args[0] + " is connected to " + proxy + ".");
- sender.sendMessage(message);
- } else {
- sender.sendMessage(PLAYER_NOT_FOUND);
- }
- } else {
- sender.sendMessage(NO_PLAYER_SPECIFIED);
- }
- }
- });
- }
- }
-
- public static class SendToAll extends Command {
- private final RedisBungee plugin;
-
- public SendToAll(RedisBungee plugin) {
- super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(CommandSender sender, String[] args) {
- if (args.length > 0) {
- String command = Joiner.on(" ").skipNulls().join(args);
- plugin.getAbstractRedisBungeeApi().sendProxyCommand(command);
- TextComponent message = new TextComponent();
- message.setColor(ChatColor.GREEN);
- message.setText("Sent the command /" + command + " to all proxies.");
- sender.sendMessage(message);
- } else {
- sender.sendMessage(NO_COMMAND_SPECIFIED);
- }
- }
- }
-
- public static class ServerId extends Command {
- private final RedisBungee plugin;
-
- public ServerId(RedisBungee plugin) {
- super("serverid", "redisbungee.command.serverid", "rserverid");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(CommandSender sender, String[] args) {
- TextComponent textComponent = new TextComponent();
- textComponent.setText("You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".");
- textComponent.setColor(ChatColor.YELLOW);
- sender.sendMessage(textComponent);
- }
- }
-
- public static class ServerIds extends Command {
- private final RedisBungee plugin;
- public ServerIds(RedisBungee plugin) {
- super("serverids", "redisbungee.command.serverids");
- this.plugin =plugin;
- }
-
- @Override
- public void execute(CommandSender sender, String[] strings) {
- TextComponent textComponent = new TextComponent();
- textComponent.setText("All server IDs: " + Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()));
- textComponent.setColor(ChatColor.YELLOW);
- sender.sendMessage(textComponent);
- }
- }
-
- public static class PlistCommand extends Command {
- private final RedisBungee plugin;
-
- public PlistCommand(RedisBungee plugin) {
- super("plist", "redisbungee.command.plist", "rplist");
- this.plugin = plugin;
- }
-
- @Override
- public void execute(final CommandSender sender, final String[] args) {
- plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
- @Override
- public void run() {
- String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getProxyId();
- if (!plugin.getProxiesIds().contains(proxy)) {
- sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
- return;
- }
- Set players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy);
- BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
- .append(playerPlural(players.size()) + " currently on proxy " + proxy + ".").create();
- if (args.length >= 2 && args[1].equals("showall")) {
- Multimap serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
- Multimap human = HashMultimap.create();
- for (Map.Entry entry : serverToPlayers.entries()) {
- if (players.contains(entry.getValue())) {
- human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
- }
- }
- for (String server : new TreeSet<>(human.keySet())) {
- TextComponent serverName = new TextComponent();
- serverName.setColor(ChatColor.RED);
- serverName.setText("[" + server + "] ");
- TextComponent serverCount = new TextComponent();
- serverCount.setColor(ChatColor.YELLOW);
- serverCount.setText("(" + human.get(server).size() + "): ");
- TextComponent serverPlayers = new TextComponent();
- serverPlayers.setColor(ChatColor.WHITE);
- serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
- sender.sendMessage(serverName, serverCount, serverPlayers);
- }
- sender.sendMessage(playersOnline);
- } else {
- sender.sendMessage(playersOnline);
- sender.sendMessage(new ComponentBuilder("To see all players online, use /plist " + proxy + " showall.").color(ChatColor.YELLOW).create());
- }
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/RedisBungee-Commands/build.gradle.kts b/RedisBungee-Commands/build.gradle.kts
new file mode 100644
index 0000000..0c5944e
--- /dev/null
+++ b/RedisBungee-Commands/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+ `java-library`
+}
+
+dependencies {
+ implementation(project(":RedisBungee-API"))
+ implementation(libs.acf.core)
+}
+
+description = "RedisBungee common commands"
+
+
+tasks {
+ compileJava {
+ options.encoding = Charsets.UTF_8.name()
+ options.release.set(17)
+ }
+ javadoc {
+ options.encoding = Charsets.UTF_8.name()
+ }
+ processResources {
+ filteringCharset = Charsets.UTF_8.name()
+ }
+}
diff --git a/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandLoader.java b/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandLoader.java
new file mode 100644
index 0000000..5346be9
--- /dev/null
+++ b/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandLoader.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.commands;
+
+import co.aikar.commands.CommandManager;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+
+import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands;
+
+public class CommandLoader {
+
+ public static void initCommands(CommandManager, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin> plugin) {
+ var commandsConfiguration = plugin.configuration().commandsConfiguration();
+ if (commandsConfiguration.redisbungeeEnabled()) {
+ commandManager.registerCommand(new CommandRedisBungee(plugin));
+ }
+ if (commandsConfiguration.redisbungeeLegacyEnabled()) {
+ commandManager.registerCommand(new LegacyRedisBungeeCommands(commandManager,plugin));
+ }
+
+ }
+
+}
diff --git a/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandRedisBungee.java b/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandRedisBungee.java
new file mode 100644
index 0000000..c67b7a0
--- /dev/null
+++ b/RedisBungee-Commands/src/main/java/com/imaginarycode/minecraft/redisbungee/commands/CommandRedisBungee.java
@@ -0,0 +1,189 @@
+/*
+ * 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.commands;
+
+import co.aikar.commands.CommandIssuer;
+import co.aikar.commands.RegisteredCommand;
+import co.aikar.commands.annotation.*;
+import com.google.common.primitives.Ints;
+import com.imaginarycode.minecraft.redisbungee.Constants;
+import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
+import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
+import com.imaginarycode.minecraft.redisbungee.commands.utils.StopperUUIDCleanupTask;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@CommandAlias("rb|redisbungee")
+@CommandPermission("redisbungee.command.use")
+@Description("Main command")
+public class CommandRedisBungee extends AdventureBaseCommand {
+
+ private final RedisBungeePlugin> plugin;
+
+ public CommandRedisBungee(RedisBungeePlugin> plugin) {
+ this.plugin = plugin;
+ }
+
+ @Default
+ @Subcommand("info|version|git")
+ @Description("information about current redisbungee build")
+ public void info(CommandIssuer issuer) {
+ final String message = """
+ This proxy is running RedisBungee Limework's fork
+ ========================================
+ RedisBungee version:
+ Commit:
+ ========================================
+ run /rb help for more commands""";
+ sendMessage(
+ issuer,
+ MiniMessage.miniMessage()
+ .deserialize(
+ message,
+ Placeholder.component("version", Component.text(Constants.VERSION)),
+ Placeholder.component(
+ "commit",
+ Component.text(Constants.GIT_COMMIT.substring(0, 8))
+ .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
+ .hoverEvent(HoverEvent.showText(Component.text("Click me to open: " + Constants.getGithubCommitLink())))
+ )));
+ }
+ //