2022-04-13 13:14:08 +00:00
|
|
|
package com.imaginarycode.minecraft.redisbungee.internal;
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2016-02-27 04:17:24 +00:00
|
|
|
import com.google.common.cache.Cache;
|
|
|
|
import com.google.common.cache.CacheBuilder;
|
2015-02-06 03:11:22 +00:00
|
|
|
import com.google.common.net.InetAddresses;
|
2022-04-13 13:14:08 +00:00
|
|
|
import com.google.common.reflect.TypeToken;
|
2016-02-28 06:15:43 +00:00
|
|
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
2022-04-13 13:14:08 +00:00
|
|
|
import com.google.gson.Gson;
|
2014-08-31 17:16:51 +00:00
|
|
|
import com.google.gson.JsonObject;
|
|
|
|
import com.google.gson.JsonParser;
|
2022-07-16 05:18:33 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.internal.util.RedisTask;
|
2014-08-10 03:16:47 +00:00
|
|
|
import redis.clients.jedis.Jedis;
|
2022-07-16 05:18:33 +00:00
|
|
|
import redis.clients.jedis.JedisCluster;
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2022-04-13 16:08:46 +00:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
2014-08-10 03:16:47 +00:00
|
|
|
import java.net.InetAddress;
|
2016-02-27 04:17:24 +00:00
|
|
|
import java.util.Objects;
|
2014-09-21 17:56:46 +00:00
|
|
|
import java.util.UUID;
|
2015-06-21 21:32:28 +00:00
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
import java.util.concurrent.ExecutionException;
|
2015-06-24 03:17:50 +00:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2014-08-10 03:16:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This class manages all the data that RedisBungee fetches from Redis, along with updates to that data.
|
|
|
|
*
|
|
|
|
* @since 0.3.3
|
|
|
|
*/
|
2022-07-07 22:39:05 +00:00
|
|
|
public abstract class AbstractDataManager<P, PL, PD, PS> {
|
2022-04-13 13:14:08 +00:00
|
|
|
private final RedisBungeePlugin<P> plugin;
|
2016-02-27 04:17:24 +00:00
|
|
|
private final Cache<UUID, String> serverCache = createCache();
|
|
|
|
private final Cache<UUID, String> proxyCache = createCache();
|
|
|
|
private final Cache<UUID, InetAddress> ipCache = createCache();
|
|
|
|
private final Cache<UUID, Long> lastOnlineCache = createCache();
|
2022-04-13 13:14:08 +00:00
|
|
|
private final Gson gson = new Gson();
|
2015-06-24 03:17:50 +00:00
|
|
|
|
2022-07-07 22:39:05 +00:00
|
|
|
public AbstractDataManager(RedisBungeePlugin<P> plugin) {
|
2015-06-24 03:17:50 +00:00
|
|
|
this.plugin = plugin;
|
|
|
|
}
|
2015-06-21 21:32:28 +00:00
|
|
|
|
2016-02-27 04:17:24 +00:00
|
|
|
private static <K, V> Cache<K, V> createCache() {
|
2016-03-08 01:56:58 +00:00
|
|
|
// TODO: Allow customization via cache specification, ala ServerListPlus
|
2016-02-27 04:17:24 +00:00
|
|
|
return CacheBuilder.newBuilder()
|
|
|
|
.maximumSize(1000)
|
|
|
|
.expireAfterWrite(1, TimeUnit.HOURS)
|
|
|
|
.build();
|
2015-06-24 03:17:50 +00:00
|
|
|
}
|
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
private final JsonParser parser = new JsonParser();
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
public String getServer(final UUID uuid) {
|
2022-04-13 13:14:08 +00:00
|
|
|
P player = plugin.getPlayer(uuid);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
|
|
|
if (player != null)
|
2022-04-13 13:14:08 +00:00
|
|
|
return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null;
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
2022-07-16 05:18:33 +00:00
|
|
|
return serverCache.get(uuid, new RedisTask<String>(plugin.getApi()) {
|
2015-06-21 21:32:28 +00:00
|
|
|
@Override
|
2022-07-16 05:18:33 +00:00
|
|
|
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");
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
}
|
|
|
|
});
|
2016-02-28 06:15:43 +00:00
|
|
|
} catch (ExecutionException | UncheckedExecutionException e) {
|
|
|
|
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
|
|
|
|
return null; // HACK
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.logFatal("Unable to get server");
|
2014-08-10 03:16:47 +00:00
|
|
|
throw new RuntimeException("Unable to get server for " + uuid, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
public String getProxy(final UUID uuid) {
|
2022-04-13 13:14:08 +00:00
|
|
|
P player = plugin.getPlayer(uuid);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
|
|
|
if (player != null)
|
2022-04-13 13:14:08 +00:00
|
|
|
return plugin.getConfiguration().getServerId();
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
2022-07-16 05:18:33 +00:00
|
|
|
return proxyCache.get(uuid, new RedisTask<String>(plugin.getApi()) {
|
2015-06-21 21:32:28 +00:00
|
|
|
@Override
|
2022-07-16 05:18:33 +00:00
|
|
|
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");
|
2015-06-21 21:32:28 +00:00
|
|
|
}
|
|
|
|
});
|
2016-02-28 06:15:43 +00:00
|
|
|
} catch (ExecutionException | UncheckedExecutionException e) {
|
2016-02-27 04:17:24 +00:00
|
|
|
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
|
|
|
|
return null; // HACK
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.logFatal("Unable to get proxy");
|
2015-06-21 21:32:28 +00:00
|
|
|
throw new RuntimeException("Unable to get proxy for " + uuid, e);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
public InetAddress getIp(final UUID uuid) {
|
2022-04-13 13:14:08 +00:00
|
|
|
P player = plugin.getPlayer(uuid);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
|
|
|
if (player != null)
|
2022-04-13 13:14:08 +00:00
|
|
|
return plugin.getPlayerIp(player);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
2022-07-16 05:18:33 +00:00
|
|
|
return ipCache.get(uuid, new RedisTask<InetAddress>(plugin.getApi()) {
|
2015-06-21 21:32:28 +00:00
|
|
|
@Override
|
2022-07-16 05:18:33 +00:00
|
|
|
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);
|
2015-06-21 21:32:28 +00:00
|
|
|
}
|
|
|
|
});
|
2016-02-28 06:15:43 +00:00
|
|
|
} catch (ExecutionException | UncheckedExecutionException e) {
|
2016-02-27 04:17:24 +00:00
|
|
|
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
|
|
|
|
return null; // HACK
|
2022-04-13 16:08:46 +00:00
|
|
|
plugin.logFatal("Unable to get IP");
|
2015-06-21 21:32:28 +00:00
|
|
|
throw new RuntimeException("Unable to get IP for " + uuid, e);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
public long getLastOnline(final UUID uuid) {
|
2022-04-13 13:14:08 +00:00
|
|
|
P player = plugin.getPlayer(uuid);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
|
|
|
if (player != null)
|
|
|
|
return 0;
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
2022-07-16 05:18:33 +00:00
|
|
|
return lastOnlineCache.get(uuid, new RedisTask<Long>(plugin.getApi()) {
|
2015-06-21 21:32:28 +00:00
|
|
|
@Override
|
2022-07-16 05:18:33 +00:00
|
|
|
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);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
2015-06-21 21:32:28 +00:00
|
|
|
});
|
|
|
|
} catch (ExecutionException e) {
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.logFatal("Unable to get last time online");
|
2015-06-21 21:32:28 +00:00
|
|
|
throw new RuntimeException("Unable to get last time online for " + uuid, e);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-13 16:22:07 +00:00
|
|
|
protected void invalidate(UUID uuid) {
|
2015-06-21 21:32:28 +00:00
|
|
|
ipCache.invalidate(uuid);
|
|
|
|
lastOnlineCache.invalidate(uuid);
|
|
|
|
serverCache.invalidate(uuid);
|
|
|
|
proxyCache.invalidate(uuid);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
2022-04-13 18:17:38 +00:00
|
|
|
|
2022-04-13 18:23:15 +00:00
|
|
|
// Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
|
2022-04-13 16:08:46 +00:00
|
|
|
public abstract void onPostLogin(PL event);
|
2022-07-16 05:18:33 +00:00
|
|
|
|
2022-04-13 18:23:15 +00:00
|
|
|
// Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
|
2022-04-13 16:08:46 +00:00
|
|
|
public abstract void onPlayerDisconnect(PD event);
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
public abstract void onPubSubMessage(PS event);
|
|
|
|
|
|
|
|
protected void handlePubSubMessage(String channel, String message) {
|
|
|
|
if (!channel.equals("redisbungee-data"))
|
2014-08-10 03:16:47 +00:00
|
|
|
return;
|
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
// Partially deserialize the message so we can look at the action
|
2022-04-13 13:14:08 +00:00
|
|
|
JsonObject jsonObject = parser.parse(message).getAsJsonObject();
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
String source = jsonObject.get("source").getAsString();
|
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
if (source.equals(plugin.getConfiguration().getServerId()))
|
2014-08-10 03:16:47 +00:00
|
|
|
return;
|
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString());
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case JOIN:
|
2022-04-13 21:40:16 +00:00
|
|
|
final DataManagerMessage<LoginPayload> message1 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<LoginPayload>>() {
|
2014-09-21 17:56:46 +00:00
|
|
|
}.getType());
|
2014-08-31 17:16:51 +00:00
|
|
|
proxyCache.put(message1.getTarget(), message1.getSource());
|
2014-09-21 17:56:46 +00:00
|
|
|
lastOnlineCache.put(message1.getTarget(), (long) 0);
|
2022-04-13 21:40:16 +00:00
|
|
|
ipCache.put(message1.getTarget(), message1.getPayload().getAddress());
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.executeAsync(new Runnable() {
|
2014-08-31 17:16:51 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2022-04-13 16:08:46 +00:00
|
|
|
Object event;
|
|
|
|
try {
|
|
|
|
event = plugin.getNetworkJoinEventClass().getDeclaredConstructor(UUID.class).newInstance(message1.getTarget());
|
2022-07-16 05:18:33 +00:00
|
|
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
|
|
|
NoSuchMethodException e) {
|
2022-04-13 16:08:46 +00:00
|
|
|
throw new RuntimeException("unable to dispatch an network join event", e);
|
|
|
|
}
|
|
|
|
plugin.callEvent(event);
|
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case LEAVE:
|
2022-04-13 21:40:16 +00:00
|
|
|
final DataManagerMessage<LogoutPayload> message2 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<LogoutPayload>>() {
|
2014-09-21 17:56:46 +00:00
|
|
|
}.getType());
|
2014-08-31 17:16:51 +00:00
|
|
|
invalidate(message2.getTarget());
|
2022-04-13 21:40:16 +00:00
|
|
|
lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp());
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.executeAsync(new Runnable() {
|
2014-08-31 17:16:51 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2022-04-13 16:08:46 +00:00
|
|
|
Object event;
|
|
|
|
try {
|
|
|
|
event = plugin.getNetworkQuitEventClass().getDeclaredConstructor(UUID.class).newInstance(message2.getTarget());
|
2022-07-16 05:18:33 +00:00
|
|
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
|
|
|
NoSuchMethodException e) {
|
2022-04-13 16:08:46 +00:00
|
|
|
throw new RuntimeException("unable to dispatch an network quit event", e);
|
|
|
|
}
|
|
|
|
plugin.callEvent(event);
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case SERVER_CHANGE:
|
2022-04-13 21:40:16 +00:00
|
|
|
final DataManagerMessage<ServerChangePayload> message3 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<ServerChangePayload>>() {
|
2014-09-21 17:56:46 +00:00
|
|
|
}.getType());
|
2022-04-13 21:40:16 +00:00
|
|
|
serverCache.put(message3.getTarget(), message3.getPayload().getServer());
|
2022-04-13 13:14:08 +00:00
|
|
|
plugin.executeAsync(new Runnable() {
|
2014-08-31 17:16:51 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2022-04-13 16:08:46 +00:00
|
|
|
Object event;
|
|
|
|
try {
|
|
|
|
event = plugin.getServerChangeEventClass().getDeclaredConstructor(UUID.class, String.class, String.class).newInstance(message3.getTarget(), ((ServerChangePayload) message3.getPayload()).getOldServer(), ((ServerChangePayload) message3.getPayload()).getServer());
|
2022-07-16 05:18:33 +00:00
|
|
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
|
|
|
NoSuchMethodException e) {
|
2022-04-13 16:08:46 +00:00
|
|
|
throw new RuntimeException("unable to dispatch an server change event", e);
|
|
|
|
}
|
|
|
|
plugin.callEvent(event);
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
2014-08-10 03:57:57 +00:00
|
|
|
}
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
|
2022-04-13 21:40:16 +00:00
|
|
|
public static class DataManagerMessage<T> {
|
2014-09-21 17:56:46 +00:00
|
|
|
private final UUID target;
|
2022-04-13 13:14:08 +00:00
|
|
|
private final String source;
|
2014-09-21 17:56:46 +00:00
|
|
|
private final Action action; // for future use!
|
2022-04-13 21:40:16 +00:00
|
|
|
private final T payload;
|
2022-04-13 13:14:08 +00:00
|
|
|
|
2022-04-13 21:40:16 +00:00
|
|
|
public DataManagerMessage(UUID target, String source, Action action, T payload) {
|
2022-04-13 13:14:08 +00:00
|
|
|
this.target = target;
|
|
|
|
this.source = source;
|
|
|
|
this.action = action;
|
|
|
|
this.payload = payload;
|
|
|
|
}
|
2015-06-24 09:51:40 +00:00
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
public UUID getTarget() {
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getSource() {
|
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Action getAction() {
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2022-04-13 21:40:16 +00:00
|
|
|
public T getPayload() {
|
2022-04-13 13:14:08 +00:00
|
|
|
return payload;
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum Action {
|
2014-08-10 03:16:47 +00:00
|
|
|
JOIN,
|
2014-08-10 03:57:57 +00:00
|
|
|
LEAVE,
|
|
|
|
SERVER_CHANGE
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
|
2022-04-13 21:40:16 +00:00
|
|
|
public static class LoginPayload {
|
2014-08-31 17:16:51 +00:00
|
|
|
private final InetAddress address;
|
2022-04-13 13:14:08 +00:00
|
|
|
|
|
|
|
public LoginPayload(InetAddress address) {
|
|
|
|
this.address = address;
|
|
|
|
}
|
|
|
|
|
|
|
|
public InetAddress getAddress() {
|
|
|
|
return address;
|
|
|
|
}
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
|
2022-07-16 05:18:33 +00:00
|
|
|
public static class ServerChangePayload {
|
2014-08-31 17:16:51 +00:00
|
|
|
private final String server;
|
2016-02-27 04:17:24 +00:00
|
|
|
private final String oldServer;
|
2022-04-13 13:14:08 +00:00
|
|
|
|
2022-04-13 16:22:07 +00:00
|
|
|
public ServerChangePayload(String server, String oldServer) {
|
2022-04-13 13:14:08 +00:00
|
|
|
this.server = server;
|
|
|
|
this.oldServer = oldServer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getServer() {
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getOldServer() {
|
|
|
|
return oldServer;
|
|
|
|
}
|
2014-08-31 17:16:51 +00:00
|
|
|
}
|
|
|
|
|
2022-04-13 13:14:08 +00:00
|
|
|
|
2022-04-13 21:40:16 +00:00
|
|
|
public static class LogoutPayload {
|
2014-08-31 17:16:51 +00:00
|
|
|
private final long timestamp;
|
2022-04-13 13:14:08 +00:00
|
|
|
|
|
|
|
public LogoutPayload(long timestamp) {
|
|
|
|
this.timestamp = timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getTimestamp() {
|
|
|
|
return timestamp;
|
|
|
|
}
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
2022-04-13 13:14:08 +00:00
|
|
|
}
|