API converted to support RedisCluster

This commit is contained in:
mohammed jasem alaajel 2022-07-16 09:18:33 +04:00
parent e986d5f1fb
commit 44f9a0945d
17 changed files with 501 additions and 253 deletions

View File

@ -6,6 +6,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisBungeeMode;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@ -311,30 +313,60 @@ public class RedisBungeeAPI {
* @since 0.7.0
*/
public Jedis requestJedis() {
return this.plugin.getJedisSummoner().requestJedis();
if (getMode() == RedisBungeeMode.SINGLE) {
return ((JedisSummoner) this.plugin.getSummoner()).obtainResource();
} else {
throw new RuntimeException("RedisBungee is on Cluster MODE!");
}
}
/**
* This gets Redis Bungee {@link JedisPool}
* @return {@link JedisPool}
* @since 0.6.5
*/
public JedisPool getJedisPool() {
return this.plugin.getJedisSummoner().getJedisPool();
if (getMode() == RedisBungeeMode.SINGLE) {
return ((JedisSummoner) this.plugin.getSummoner()).getJedisPool();
} else {
throw new RuntimeException("RedisBungee is on Cluster MODE!");
}
}
/**
* This gets Redis Bungee {@link JedisPool}
* @return {@link JedisPool}
* @since 0.6.5
*/
public JedisSummoner getJedisSummoner() {
return this.plugin.getJedisSummoner();
}
/**
* returns Summoner class responsible for Single Jedis {@link Jedis}, Cluster Jedis {@link redis.clients.jedis.JedisCluster} handling
*
* @return {@link Summoner}
* @since 0.8.0
*/
public Summoner<?> getSummoner() {
return this.plugin.getSummoner();
}
/**
* This gives you instance of Jedis Cluster
* @return {@link redis.clients.jedis.JedisCluster}
* @since 0.8.0
*/
public Jedis requestClusterJedis() {
if (getMode() == RedisBungeeMode.CLUSTER) {
return ((JedisSummoner) this.plugin.getSummoner()).obtainResource();
} else {
throw new RuntimeException("RedisBungee is on single MODE!");
}
}
/**
* shows what mode is RedisBungee is on
* @return {@link RedisBungeeMode}
* @since 0.8.0
*/
public RedisBungeeMode getMode() {
return this.plugin.getRedisBungeeMode();
}
/**
* Api instance
* @return the API instance.
* @since 0.6.5
*/

View File

@ -8,7 +8,9 @@ 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.internal.util.RedisTask;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
@ -52,12 +54,17 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null;
try {
return serverCache.get(uuid, new Callable<String>() {
return serverCache.get(uuid, new RedisTask<String>(plugin.getApi()) {
@Override
public String call() throws Exception {
try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found");
}
public String singleJedisTask(Jedis jedis) {
return Objects.requireNonNull(jedis.hget("player:" + uuid, "server"), "user not found");
}
@Override
public String clusterJedisTask(JedisCluster jedisCluster) {
return Objects.requireNonNull(jedisCluster.hget("player:" + uuid, "server"), "user not found");
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
@ -76,12 +83,15 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
return plugin.getConfiguration().getServerId();
try {
return proxyCache.get(uuid, new Callable<String>() {
return proxyCache.get(uuid, new RedisTask<String>(plugin.getApi()) {
@Override
public String call() throws Exception {
try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found");
}
public String singleJedisTask(Jedis jedis) {
return Objects.requireNonNull(jedis.hget("player:" + uuid, "proxy"), "user not found");
}
@Override
public String clusterJedisTask(JedisCluster jedisCluster) {
return Objects.requireNonNull(jedisCluster.hget("player:" + uuid, "proxy"), "user not found");
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
@ -99,15 +109,21 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
return plugin.getPlayerIp(player);
try {
return ipCache.get(uuid, new Callable<InetAddress>() {
return ipCache.get(uuid, new RedisTask<InetAddress>(plugin.getApi()) {
@Override
public InetAddress call() throws Exception {
try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) {
String result = tmpRsc.hget("player:" + uuid, "ip");
if (result == null)
throw new NullPointerException("user not found");
return InetAddresses.forString(result);
}
public InetAddress singleJedisTask(Jedis jedis) {
String result = jedis.hget("player:" + uuid, "ip");
if (result == null)
throw new NullPointerException("user not found");
return InetAddresses.forString(result);
}
@Override
public InetAddress clusterJedisTask(JedisCluster jedisCluster) {
String result = jedisCluster.hget("player:" + uuid, "ip");
if (result == null)
throw new NullPointerException("user not found");
return InetAddresses.forString(result);
}
});
} catch (ExecutionException | UncheckedExecutionException e) {
@ -125,13 +141,17 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
return 0;
try {
return lastOnlineCache.get(uuid, new Callable<Long>() {
return lastOnlineCache.get(uuid, new RedisTask<Long>(plugin.getApi()) {
@Override
public Long call() throws Exception {
try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) {
String result = tmpRsc.hget("player:" + uuid, "online");
return result == null ? -1 : Long.valueOf(result);
}
public Long singleJedisTask(Jedis jedis) {
String result = jedis.hget("player:" + uuid, "online");
return result == null ? -1 : Long.parseLong(result);
}
@Override
public Long clusterJedisTask(JedisCluster jedisCluster) {
String result = jedisCluster.hget("player:" + uuid, "online");
return result == null ? -1 : Long.parseLong(result);
}
});
} catch (ExecutionException e) {
@ -149,6 +169,7 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
// 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);
@ -181,7 +202,8 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
Object event;
try {
event = plugin.getNetworkJoinEventClass().getDeclaredConstructor(UUID.class).newInstance(message1.getTarget());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an network join event", e);
}
plugin.callEvent(event);
@ -200,7 +222,8 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
Object event;
try {
event = plugin.getNetworkQuitEventClass().getDeclaredConstructor(UUID.class).newInstance(message2.getTarget());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an network quit event", e);
}
plugin.callEvent(event);
@ -217,7 +240,8 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
Object event;
try {
event = plugin.getServerChangeEventClass().getDeclaredConstructor(UUID.class, String.class, String.class).newInstance(message3.getTarget(), ((ServerChangePayload) message3.getPayload()).getOldServer(), ((ServerChangePayload) message3.getPayload()).getServer());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an server change event", e);
}
plugin.callEvent(event);
@ -275,7 +299,7 @@ public abstract class AbstractDataManager<P, PL, PD, PS> {
}
}
public static class ServerChangePayload{
public static class ServerChangePayload {
private final String server;
private final String oldServer;

View File

@ -1,13 +1,14 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisTask;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class PubSubListener implements Runnable {
private JedisPubSubHandler jpsh;
@ -21,27 +22,58 @@ public class PubSubListener implements Runnable {
@Override
public void run() {
try (Jedis rsc = plugin.getJedisSummoner().requestJedis()) {
try {
jpsh = new JedisPubSubHandler(plugin);
addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
rsc.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
plugin.logWarn("PubSub error, attempting to recover.");
RedisTask<Void> subTask = new RedisTask<Void>(plugin.getApi()) {
@Override
public Void singleJedisTask(Jedis jedis) {
try {
jpsh.unsubscribe();
} catch (Exception e1) {
jpsh = new JedisPubSubHandler(plugin);
addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
jedis.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
plugin.logWarn("PubSub error, attempting to recover.");
try {
jpsh.unsubscribe();
} catch (Exception e1) {
/* This may fail with
- java.net.SocketException: Broken pipe
- redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance
*/
}
}
return null;
}
@Override
public Void clusterJedisTask(JedisCluster jedisCluster) {
try {
jpsh = new JedisPubSubHandler(plugin);
addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
jedisCluster.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
plugin.logWarn("PubSub error, attempting to recover.");
try {
jpsh.unsubscribe();
} catch (Exception e1) {
/* This may fail with
- java.net.SocketException: Broken pipe
- redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance
*/
}
}
return null;
}
};
try {
subTask.execute();
} catch (JedisConnectionException e) {
plugin.logWarn("PubSub error, attempting to recover in 5 secs.");
plugin.executeAsyncAfter(this, TimeUnit.SECONDS, 5);

View File

@ -2,7 +2,8 @@ package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator;
import redis.clients.jedis.Jedis;
@ -31,11 +32,7 @@ public interface RedisBungeePlugin<P> extends EventsPlatform{
}
Jedis requestJedis();
boolean isJedisAvailable();
JedisSummoner getJedisSummoner();
Summoner<?> getSummoner();
RedisBungeeConfiguration getConfiguration();
@ -101,4 +98,6 @@ public interface RedisBungeePlugin<P> extends EventsPlatform{
void loadConfig() throws Exception;
RedisBungeeMode getRedisBungeeMode();
}

View File

@ -0,0 +1,30 @@
package com.imaginarycode.minecraft.redisbungee.internal.summoners;
import redis.clients.jedis.JedisCluster;
import java.io.IOException;
public class ClusterJedisSummoner implements Summoner<JedisCluster> {
public final JedisCluster jedisCluster;
private boolean closed = false;
public ClusterJedisSummoner(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}
@Override
public JedisCluster obtainResource() {
return jedisCluster;
}
@Override
public boolean isAvailable() {
return !closed;
}
@Override
public void close() throws IOException {
this.closed = true;
jedisCluster.close();
}
}

View File

@ -1,25 +1,35 @@
package com.imaginarycode.minecraft.redisbungee.internal.summoners;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import java.io.Closeable;
import java.io.IOException;
public class JedisSummoner implements Summoner<Jedis> {
/**
* This class intended for future release to support redis sentinel or redis clusters
*
* @author Ham1255
* @since 0.7.0
*
*/
public interface JedisSummoner extends Closeable {
private final JedisPool jedisPool;
Jedis requestJedis();
public JedisSummoner(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
boolean isJedisAvailable();
@Override
public Jedis obtainResource() {
return jedisPool.getResource();
}
JedisPool getJedisPool();
public JedisPool getJedisPool() {
return this.jedisPool;
}
@Override
public boolean isAvailable() {
return !jedisPool.isClosed();
}
@Override
public void close() throws IOException {
this.jedisPool.close();
}
}

View File

@ -1,34 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.internal.summoners;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.io.IOException;
public class SinglePoolJedisSummoner implements JedisSummoner {
final JedisPool jedisPool;
public SinglePoolJedisSummoner(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
@Override
public Jedis requestJedis() {
return this.jedisPool.getResource();
}
@Override
public boolean isJedisAvailable() {
return !this.jedisPool.isClosed();
}
@Override
public JedisPool getJedisPool() {
return this.jedisPool;
}
@Override
public void close() throws IOException {
jedisPool.close();
}
}

View File

@ -0,0 +1,20 @@
package com.imaginarycode.minecraft.redisbungee.internal.summoners;
import java.io.Closeable;
/**
* This class intended for future release to support redis sentinel or redis clusters
*
* @author Ham1255
* @since 0.7.0
*
*/
public interface Summoner<P> extends Closeable {
P obtainResource();
boolean isAvailable();
}

View File

@ -2,6 +2,7 @@ package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.exceptions.JedisDataException;
import java.util.List;
@ -14,10 +15,20 @@ public class LuaManager {
}
public Script createScript(String script) {
try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) {
String hash = jedis.scriptLoad(script);
return new Script(script, hash);
}
RedisTask<Script> scriptRedisTask = new RedisTask<Script>(plugin.getApi()) {
@Override
public Script singleJedisTask(Jedis jedis) {
String hash = jedis.scriptLoad(script);
return new Script(script, hash);
}
@Override
public Script clusterJedisTask(JedisCluster jedisCluster) {
String hash = jedisCluster.scriptLoad(script, null);
return new Script(script, hash);
}
};
return scriptRedisTask.execute();
}
public class Script {
@ -38,21 +49,40 @@ public class LuaManager {
}
public Object eval(List<String> keys, List<String> args) {
Object data;
try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) {
try {
data = jedis.evalsha(hashed, keys, args);
} catch (JedisDataException e) {
if (e.getMessage().startsWith("NOSCRIPT")) {
data = jedis.eval(script, keys, args);
} else {
throw e;
RedisTask<Object> objectRedisTask = new RedisTask<Object>(plugin.getApi()) {
@Override
public Object singleJedisTask(Jedis jedis) {
Object data;
try {
data = jedis.evalsha(hashed, keys, args);
} catch (JedisDataException e) {
if (e.getMessage().startsWith("NOSCRIPT")) {
data = jedis.eval(script, keys, args);
} else {
throw e;
}
}
return data;
}
}
return data;
@Override
public Object clusterJedisTask(JedisCluster jedisCluster) {
Object data;
try {
data = jedisCluster.evalsha(hashed, keys, args);
} catch (JedisDataException e) {
if (e.getMessage().startsWith("NOSCRIPT")) {
data = jedisCluster.eval(script, keys, args);
} else {
throw e;
}
}
return data;
}
};
return objectRedisTask.execute();
}
}
}

View File

@ -0,0 +1,5 @@
package com.imaginarycode.minecraft.redisbungee.internal.util;
public enum RedisBungeeMode {
SINGLE, CLUSTER
}

View File

@ -1,48 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.concurrent.Callable;
public abstract class RedisCallable<T> implements Callable<T>, Runnable {
private final RedisBungeePlugin<?> plugin;
public RedisCallable(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Override
public T call() {
return run(false);
}
public void run() {
call();
}
private T run(boolean retry) {
try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) {
return call(jedis);
} catch (JedisConnectionException e) {
plugin.logFatal("Unable to get connection");
if (!retry) {
// Wait one second before retrying the task
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
throw new RuntimeException("task failed to run", e1);
}
return run(true);
}
}
throw new RuntimeException("task failed to run");
}
protected abstract T call(Jedis jedis);
}

View File

@ -0,0 +1,50 @@
package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.ClusterJedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import java.util.concurrent.Callable;
public abstract class RedisTask<V> implements Runnable, Callable<V> {
private final Summoner<?> summoner;
private final RedisBungeeAPI api;
@Override
public V call() throws Exception {
return execute();
}
public RedisTask(RedisBungeeAPI api) {
this.api = api;
this.summoner = api.getSummoner();
}
public abstract V singleJedisTask(Jedis jedis);
public abstract V clusterJedisTask(JedisCluster jedisCluster);
@Override
public void run() {
this.execute();
}
public V execute(){
if (api.getMode() == RedisBungeeMode.SINGLE) {
JedisSummoner jedisSummoner = (JedisSummoner) summoner;
try (Jedis jedis = jedisSummoner.obtainResource()) {
return this.singleJedisTask(jedis);
}
} else if (api.getMode() == RedisBungeeMode.CLUSTER) {
ClusterJedisSummoner clusterJedisSummoner = (ClusterJedisSummoner) summoner;
return this.clusterJedisTask(clusterJedisSummoner.obtainResource());
}
return null;
}
}

View File

@ -4,16 +4,18 @@ import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisTask;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.regex.Pattern;
public final class UUIDTranslator {
@ -41,7 +43,7 @@ public final class UUIDTranslator {
uuidToNameMap.put(uuid, entry);
}
public final UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
public UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
@ -71,43 +73,88 @@ public final class UUIDTranslator {
if (!plugin.isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
}
RedisTask<UUID> redisTask = new RedisTask<UUID>(plugin.getApi()) {
@Override
public UUID singleJedisTask(Jedis jedis) {
String stored = jedis.hget("uuid-cache", player.toLowerCase());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedis.hdel("uuid-cache", player.toLowerCase());
// Doesn't hurt to also remove the UUID entry as well.
jedis.hdel("uuid-cache", entry.getUuid().toString());
} else {
nameToUuidMap.put(player.toLowerCase(), entry);
uuidToNameMap.put(entry.getUuid(), entry);
return entry.getUuid();
}
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) {
plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
return null;
}
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(player)) {
persistInfo(entry.getKey(), entry.getValue(), jedis);
return entry.getValue();
}
}
return null;
}
@Override
public UUID clusterJedisTask(JedisCluster jedisCluster) {
String stored = jedisCluster.hget("uuid-cache", player.toLowerCase());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedisCluster.hdel("uuid-cache", player.toLowerCase());
// Doesn't hurt to also remove the UUID entry as well.
jedisCluster.hdel("uuid-cache", entry.getUuid().toString());
} else {
nameToUuidMap.put(player.toLowerCase(), entry);
uuidToNameMap.put(entry.getUuid(), entry);
return entry.getUuid();
}
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) {
plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
return null;
}
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(player)) {
persistInfo(entry.getKey(), entry.getValue(), jedisCluster);
return entry.getValue();
}
}
return null;
}
};
// Let's try Redis.
try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) {
String stored = jedis.hget("uuid-cache", player.toLowerCase());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedis.hdel("uuid-cache", player.toLowerCase());
// Doesn't hurt to also remove the UUID entry as well.
jedis.hdel("uuid-cache", entry.getUuid().toString());
} else {
nameToUuidMap.put(player.toLowerCase(), entry);
uuidToNameMap.put(entry.getUuid(), entry);
return entry.getUuid();
}
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) {
plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
return null;
}
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(player)) {
persistInfo(entry.getKey(), entry.getValue(), jedis);
return entry.getValue();
}
}
try {
return redisTask.execute();
} catch (JedisException e) {
plugin.logFatal("Unable to fetch UUID for " + player);
}
@ -115,7 +162,7 @@ public final class UUIDTranslator {
return null; // Nope, game over!
}
public final String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
@ -130,61 +177,108 @@ public final class UUIDTranslator {
uuidToNameMap.remove(player);
}
// Okay, it wasn't locally cached. Let's try Redis.
try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) {
String stored = jedis.hget("uuid-cache", player.toString());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
RedisTask<String> redisTask = new RedisTask<String>(plugin.getApi()) {
@Override
public String singleJedisTask(Jedis jedis) {
String stored = jedis.hget("uuid-cache", player.toString());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry:
if (entry.expired()) {
jedis.hdel("uuid-cache", player.toString());
// Doesn't hurt to also remove the named entry as well.
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches.
jedis.hdel("uuid-cache", entry.getName());
} else {
nameToUuidMap.put(entry.getName().toLowerCase(), entry);
uuidToNameMap.put(player, entry);
return entry.getName();
// Check for expiry:
if (entry.expired()) {
jedis.hdel("uuid-cache", player.toString());
// Doesn't hurt to also remove the named entry as well.
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches.
jedis.hdel("uuid-cache", entry.getName());
} else {
nameToUuidMap.put(entry.getName().toLowerCase(), entry);
uuidToNameMap.put(player, entry);
return entry.getName();
}
}
}
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
// That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane.
String name;
try {
List<String> nameHist = NameFetcher.nameHistoryFromUuid(player);
name = Iterables.getLast(nameHist, null);
} catch (Exception e) {
plugin.logFatal("Unable to fetch name from Mojang for " + player);
// That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane.
String name;
try {
List<String> nameHist = NameFetcher.nameHistoryFromUuid(player);
name = Iterables.getLast(nameHist, null);
} catch (Exception e) {
plugin.logFatal("Unable to fetch name from Mojang for " + player);
return null;
}
if (name != null) {
persistInfo(name, player, jedis);
return name;
}
return null;
}
if (name != null) {
persistInfo(name, player, jedis);
return name;
}
@Override
public String clusterJedisTask(JedisCluster jedisCluster) {
String stored = jedisCluster.hget("uuid-cache", player.toString());
if (stored != null) {
// Found an entry value. Deserialize it.
CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
return null;
// Check for expiry:
if (entry.expired()) {
jedisCluster.hdel("uuid-cache", player.toString());
// Doesn't hurt to also remove the named entry as well.
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches.
jedisCluster.hdel("uuid-cache", entry.getName());
} else {
nameToUuidMap.put(entry.getName().toLowerCase(), entry);
uuidToNameMap.put(player, entry);
return entry.getName();
}
}
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
// That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane.
String name;
try {
List<String> nameHist = NameFetcher.nameHistoryFromUuid(player);
name = Iterables.getLast(nameHist, null);
} catch (Exception e) {
plugin.logFatal("Unable to fetch name from Mojang for " + player);
return null;
}
if (name != null) {
persistInfo(name, player, jedisCluster);
return name;
}
return null;
}
};
// Okay, it wasn't locally cached. Let's try Redis.
try {
return redisTask.execute();
} catch (JedisException e) {
plugin.logFatal("Unable to fetch name for " + player);
return null;
}
}
public final void persistInfo(String name, UUID uuid, Jedis jedis) {
public void persistInfo(String name, UUID uuid, Jedis jedis) {
addToMaps(name, uuid);
String json = gson.toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
}
public final void persistInfo(String name, UUID uuid, Pipeline jedis) {
public void persistInfo(String name, UUID uuid, JedisCluster jedisCluster) {
addToMaps(name, uuid);
String json = gson.toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
jedisCluster.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
}
private static class CachedUUIDEntry {

View File

@ -11,7 +11,6 @@ import com.imaginarycode.minecraft.redisbungee.internal.AbstractDataManager;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.internal.RedisUtil;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisCallable;
import net.md_5.bungee.api.AbstractReconnectHandler;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@ -21,6 +20,7 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.Pipeline;
import java.net.InetAddress;
@ -125,6 +125,11 @@ public class RedisBungeeBungeeListener extends AbstractRedisBungeeListener<Login
new AbstractDataManager.ServerChangePayload(event.getServer().getInfo().getName(), currentServer))));
return null;
}
@Override
protected Void call(JedisCluster jedisCluster) {
return null;
}
});
}

View File

@ -12,7 +12,7 @@ import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetwork
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.internal.*;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.SinglePoolJedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.util.IOUtil;
import com.imaginarycode.minecraft.redisbungee.internal.util.LuaManager;
@ -48,7 +48,7 @@ import static com.google.common.base.Preconditions.checkArgument;
public class RedisBungeeBungeePlugin extends Plugin implements RedisBungeePlugin<ProxiedPlayer> {
private RedisBungeeAPI api;
private PubSubListener psl = null;
private JedisSummoner jedisSummoner;
private Summoner jedisSummoner;
private UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
private BungeeDataManager dataManager;
@ -503,7 +503,7 @@ public class RedisBungeeBungeePlugin extends Plugin implements RedisBungeePlugin
}
@Override
public JedisSummoner getJedisSummoner() {
public Summoner getSummoner() {
return this.jedisSummoner;
}

View File

@ -11,7 +11,6 @@ import com.imaginarycode.minecraft.redisbungee.internal.AbstractDataManager;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.internal.RedisUtil;
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisCallable;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.ResultedEvent;

View File

@ -13,7 +13,7 @@ import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetwork
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.internal.*;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.internal.summoners.SinglePoolJedisSummoner;
import com.imaginarycode.minecraft.redisbungee.internal.util.IOUtil;
import com.imaginarycode.minecraft.redisbungee.internal.util.LuaManager;
@ -61,7 +61,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
private final Path dataFolder;
private final RedisBungeeAPI api;
private final PubSubListener psl;
private JedisSummoner jedisSummoner;
private Summoner jedisSummoner;
private final UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
private final VelocityDataManager dataManager;
@ -201,7 +201,7 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
}
@Override
public JedisSummoner getJedisSummoner() {
public Summoner getSummoner() {
return this.jedisSummoner;
}