2014-08-10 03:16:47 +00:00
|
|
|
/**
|
2015-06-09 23:37:01 +00:00
|
|
|
* This is free and unencumbered software released into the public domain.
|
2015-06-24 09:51:40 +00:00
|
|
|
* <p/>
|
2015-06-09 23:37:01 +00:00
|
|
|
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
|
|
* distribute this software, either in source code form or as a compiled
|
|
|
|
* binary, for any purpose, commercial or non-commercial, and by any
|
|
|
|
* means.
|
2015-06-24 09:51:40 +00:00
|
|
|
* <p/>
|
2015-06-09 23:37:01 +00:00
|
|
|
* In jurisdictions that recognize copyright laws, the author or authors
|
|
|
|
* of this software dedicate any and all copyright interest in the
|
|
|
|
* software to the public domain. We make this dedication for the benefit
|
|
|
|
* of the public at large and to the detriment of our heirs and
|
|
|
|
* successors. We intend this dedication to be an overt act of
|
|
|
|
* relinquishment in perpetuity of all present and future rights to this
|
|
|
|
* software under copyright law.
|
2015-06-24 09:51:40 +00:00
|
|
|
* <p/>
|
2015-06-09 23:37:01 +00:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
2015-06-24 09:51:40 +00:00
|
|
|
* <p/>
|
2015-06-09 23:37:01 +00:00
|
|
|
* For more information, please refer to <http://unlicense.org/>
|
2014-08-10 03:16:47 +00:00
|
|
|
*/
|
|
|
|
package com.imaginarycode.minecraft.redisbungee;
|
|
|
|
|
2015-02-06 03:11:22 +00:00
|
|
|
import com.google.common.net.InetAddresses;
|
2014-08-31 17:16:51 +00:00
|
|
|
import com.google.gson.JsonObject;
|
|
|
|
import com.google.gson.JsonParser;
|
|
|
|
import com.google.gson.reflect.TypeToken;
|
|
|
|
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
|
|
|
|
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
|
|
|
|
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
|
2014-08-10 03:16:47 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
2015-06-21 21:32:28 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.util.InternalCache;
|
2014-08-10 03:16:47 +00:00
|
|
|
import lombok.Getter;
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
|
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
|
|
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
|
|
|
import net.md_5.bungee.api.plugin.Listener;
|
|
|
|
import net.md_5.bungee.event.EventHandler;
|
|
|
|
import redis.clients.jedis.Jedis;
|
|
|
|
|
|
|
|
import java.net.InetAddress;
|
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
|
|
|
import java.util.logging.Level;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class manages all the data that RedisBungee fetches from Redis, along with updates to that data.
|
|
|
|
*
|
|
|
|
* @since 0.3.3
|
|
|
|
*/
|
|
|
|
public class DataManager implements Listener {
|
|
|
|
private final RedisBungee plugin;
|
2015-06-24 03:17:50 +00:00
|
|
|
// TODO: Add cleanup for this.
|
2015-06-21 21:32:28 +00:00
|
|
|
private final InternalCache<UUID, String> serverCache = createCache();
|
2015-06-24 03:17:50 +00:00
|
|
|
private final InternalCache<UUID, String> proxyCache = createCache(TimeUnit.MINUTES.toMillis(60));
|
|
|
|
private final InternalCache<UUID, InetAddress> ipCache = createCache(TimeUnit.MINUTES.toMillis(60));
|
|
|
|
private final InternalCache<UUID, Long> lastOnlineCache = createCache(TimeUnit.MINUTES.toMillis(60));
|
|
|
|
|
|
|
|
public DataManager(RedisBungee plugin) {
|
|
|
|
this.plugin = plugin;
|
|
|
|
plugin.getProxy().getScheduler().schedule(plugin, new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
proxyCache.cleanup();
|
|
|
|
ipCache.cleanup();
|
|
|
|
lastOnlineCache.cleanup();
|
|
|
|
}
|
|
|
|
}, 1, 1, TimeUnit.MINUTES);
|
|
|
|
}
|
2015-06-21 21:32:28 +00:00
|
|
|
|
|
|
|
public static <K, V> InternalCache<K, V> createCache() {
|
|
|
|
return new InternalCache<>();
|
|
|
|
}
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-24 03:17:50 +00:00
|
|
|
public static <K, V> InternalCache<K, V> createCache(long entryWriteExpiry) {
|
|
|
|
return new InternalCache<>(entryWriteExpiry);
|
|
|
|
}
|
|
|
|
|
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) {
|
2014-08-10 03:16:47 +00:00
|
|
|
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
|
|
|
|
|
|
|
|
if (player != null)
|
|
|
|
return player.getServer() != null ? player.getServer().getInfo().getName() : null;
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
|
|
|
return serverCache.get(uuid, new Callable<String>() {
|
|
|
|
@Override
|
|
|
|
public String call() throws Exception {
|
|
|
|
try (Jedis tmpRsc = plugin.getPool().getResource()) {
|
|
|
|
return tmpRsc.hget("player:" + uuid, "server");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
plugin.getLogger().log(Level.SEVERE, "Unable to get server", e);
|
2014-08-10 03:16:47 +00:00
|
|
|
throw new RuntimeException("Unable to get server for " + uuid, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
public String getProxy(final UUID uuid) {
|
2014-08-10 03:16:47 +00:00
|
|
|
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
|
|
|
|
|
|
|
|
if (player != null)
|
2015-04-18 14:13:02 +00:00
|
|
|
return RedisBungee.getConfiguration().getServerId();
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
|
|
|
return proxyCache.get(uuid, new Callable<String>() {
|
|
|
|
@Override
|
|
|
|
public String call() throws Exception {
|
|
|
|
try (Jedis tmpRsc = plugin.getPool().getResource()) {
|
|
|
|
return tmpRsc.hget("player:" + uuid, "proxy");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
plugin.getLogger().log(Level.SEVERE, "Unable to get proxy", e);
|
|
|
|
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) {
|
2014-08-10 03:16:47 +00:00
|
|
|
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
|
|
|
|
|
|
|
|
if (player != null)
|
|
|
|
return player.getAddress().getAddress();
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
|
|
|
return ipCache.get(uuid, new Callable<InetAddress>() {
|
|
|
|
@Override
|
|
|
|
public InetAddress call() throws Exception {
|
|
|
|
try (Jedis tmpRsc = plugin.getPool().getResource()) {
|
|
|
|
String result = tmpRsc.hget("player:" + uuid, "ip");
|
|
|
|
return result == null ? null : InetAddresses.forString(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
plugin.getLogger().log(Level.SEVERE, "Unable to get IP", e);
|
|
|
|
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) {
|
2014-08-10 03:16:47 +00:00
|
|
|
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid);
|
|
|
|
|
|
|
|
if (player != null)
|
|
|
|
return 0;
|
|
|
|
|
2015-06-21 21:32:28 +00:00
|
|
|
try {
|
|
|
|
return lastOnlineCache.get(uuid, new Callable<Long>() {
|
|
|
|
@Override
|
|
|
|
public Long call() throws Exception {
|
|
|
|
try (Jedis tmpRsc = plugin.getPool().getResource()) {
|
|
|
|
String result = tmpRsc.hget("player:" + uuid, "online");
|
|
|
|
return result == null ? -1 : Long.valueOf(result);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-21 21:32:28 +00:00
|
|
|
});
|
|
|
|
} catch (ExecutionException e) {
|
|
|
|
plugin.getLogger().log(Level.SEVERE, "Unable to get last time online", e);
|
|
|
|
throw new RuntimeException("Unable to get last time online for " + uuid, e);
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private 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
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onPostLogin(PostLoginEvent event) {
|
|
|
|
// Invalidate all entries related to this player, since they now lie.
|
|
|
|
invalidate(event.getPlayer().getUniqueId());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
|
|
|
// Invalidate all entries related to this player, since they now lie.
|
|
|
|
invalidate(event.getPlayer().getUniqueId());
|
|
|
|
}
|
|
|
|
|
|
|
|
@EventHandler
|
|
|
|
public void onPubSubMessage(PubSubMessageEvent event) {
|
|
|
|
if (!event.getChannel().equals("redisbungee-data"))
|
|
|
|
return;
|
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
// Partially deserialize the message so we can look at the action
|
|
|
|
JsonObject jsonObject = parser.parse(event.getMessage()).getAsJsonObject();
|
2014-08-10 03:16:47 +00:00
|
|
|
|
2014-08-31 17:16:51 +00:00
|
|
|
String source = jsonObject.get("source").getAsString();
|
|
|
|
|
2015-04-18 14:13:02 +00:00
|
|
|
if (source.equals(RedisBungee.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:
|
2014-09-21 17:56:46 +00:00
|
|
|
final DataManagerMessage<LoginPayload> message1 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LoginPayload>>() {
|
|
|
|
}.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);
|
2014-08-31 17:16:51 +00:00
|
|
|
ipCache.put(message1.getTarget(), message1.getPayload().getAddress());
|
|
|
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case LEAVE:
|
2014-09-21 17:56:46 +00:00
|
|
|
final DataManagerMessage<LogoutPayload> message2 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LogoutPayload>>() {
|
|
|
|
}.getType());
|
2014-08-31 17:16:51 +00:00
|
|
|
invalidate(message2.getTarget());
|
|
|
|
lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp());
|
|
|
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case SERVER_CHANGE:
|
2015-06-24 09:51:40 +00:00
|
|
|
final DataManagerMessage<ServerChangePayload> message3 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<ServerChangePayload>>() {
|
2014-09-21 17:56:46 +00:00
|
|
|
}.getType());
|
2014-12-07 21:15:39 +00:00
|
|
|
final String oldServer = serverCache.put(message3.getTarget(), message3.getPayload().getServer());
|
2014-08-31 17:16:51 +00:00
|
|
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-12-07 21:15:39 +00:00
|
|
|
plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), oldServer, message3.getPayload().getServer()));
|
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
|
|
|
}
|
|
|
|
|
|
|
|
@Getter
|
|
|
|
@RequiredArgsConstructor
|
2014-08-31 17:16:51 +00:00
|
|
|
static class DataManagerMessage<T> {
|
2014-09-21 17:56:46 +00:00
|
|
|
private final UUID target;
|
|
|
|
private final String source = RedisBungee.getApi().getServerId();
|
|
|
|
private final Action action; // for future use!
|
|
|
|
private final T payload;
|
2015-06-24 09:51:40 +00:00
|
|
|
|
2014-08-10 03:16:47 +00:00
|
|
|
enum Action {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
@Getter
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
static class LoginPayload {
|
|
|
|
private final InetAddress address;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Getter
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
static class ServerChangePayload {
|
|
|
|
private final String server;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Getter
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
static class LogoutPayload {
|
|
|
|
private final long timestamp;
|
2014-08-10 03:16:47 +00:00
|
|
|
}
|
|
|
|
}
|