mirror of
https://github.com/proxiodev/RedisBungee.git
synced 2024-11-23 04:28:01 +00:00
RedisBungee 0.3 base code. A lot has changed. There is more to come.
This commit is contained in:
parent
d3a6170e78
commit
1362739b27
2
pom.xml
2
pom.xml
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<groupId>com.imaginarycode.minecraft</groupId>
|
<groupId>com.imaginarycode.minecraft</groupId>
|
||||||
<artifactId>RedisBungee</artifactId>
|
<artifactId>RedisBungee</artifactId>
|
||||||
<version>0.2.6-SNAPSHOT</version>
|
<version>0.3-SNAPSHOT</version>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
|
@ -12,7 +12,9 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.util.UUIDTranslator;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
@ -31,10 +33,7 @@ import redis.clients.jedis.exceptions.JedisException;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -51,6 +50,12 @@ public final class RedisBungee extends Plugin {
|
|||||||
private static Configuration configuration;
|
private static Configuration configuration;
|
||||||
@Getter
|
@Getter
|
||||||
private JedisPool pool;
|
private JedisPool pool;
|
||||||
|
@Getter
|
||||||
|
private RedisBungeeConsumer consumer;
|
||||||
|
@Getter
|
||||||
|
private UUIDTranslator uuidTranslator;
|
||||||
|
@Getter
|
||||||
|
private static Gson gson = new Gson();
|
||||||
private static RedisBungeeAPI api;
|
private static RedisBungeeAPI api;
|
||||||
private static PubSubListener psl = null;
|
private static PubSubListener psl = null;
|
||||||
private List<String> serverIds;
|
private List<String> serverIds;
|
||||||
@ -81,7 +86,7 @@ public final class RedisBungee extends Plugin {
|
|||||||
for (Map.Entry<String, String> entry : heartbeats.entrySet()) {
|
for (Map.Entry<String, String> entry : heartbeats.entrySet()) {
|
||||||
try {
|
try {
|
||||||
long stamp = Long.valueOf(entry.getValue());
|
long stamp = Long.valueOf(entry.getValue());
|
||||||
if (stamp + 30000 < System.currentTimeMillis())
|
if (System.currentTimeMillis() < stamp + 30000)
|
||||||
servers.add(entry.getKey());
|
servers.add(entry.getKey());
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
}
|
}
|
||||||
@ -99,9 +104,9 @@ public final class RedisBungee extends Plugin {
|
|||||||
return psl;
|
return psl;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Multimap<String, String> serversToPlayers() {
|
final Multimap<String, UUID> serversToPlayers() {
|
||||||
ImmutableMultimap.Builder<String, String> multimapBuilder = ImmutableMultimap.builder();
|
ImmutableMultimap.Builder<String, UUID> multimapBuilder = ImmutableMultimap.builder();
|
||||||
for (String p : getPlayers()) {
|
for (UUID p : getPlayers()) {
|
||||||
ServerInfo si = getServerFor(p);
|
ServerInfo si = getServerFor(p);
|
||||||
if (si != null)
|
if (si != null)
|
||||||
multimapBuilder = multimapBuilder.put(si.getName(), p);
|
multimapBuilder = multimapBuilder.put(si.getName(), p);
|
||||||
@ -118,14 +123,18 @@ public final class RedisBungee extends Plugin {
|
|||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis rsc = pool.getResource();
|
Jedis rsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
for (String i : getServerIds()) {
|
List<String> serverIds = getServerIds();
|
||||||
if (i.equals(configuration.getString("server-id"))) continue;
|
Map<String, String> counts = rsc.hgetAll("playerCounts");
|
||||||
if (rsc.exists("server:" + i + ":playerCount"))
|
for (Map.Entry<String, String> entry : counts.entrySet()) {
|
||||||
|
if (!serverIds.contains(entry.getKey()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entry.getKey().equals(configuration.getString("server-id"))) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
c += Integer.valueOf(rsc.get("server:" + i + ":playerCount"));
|
c += Integer.valueOf(entry.getValue());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
getLogger().severe("I found a funny number for " + i + "'s player count. Resetting it to 0.");
|
rsc.hset("playerCounts", entry.getKey(), "0");
|
||||||
rsc.set("server:" + i + ":playerCount", "0");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
@ -140,23 +149,35 @@ public final class RedisBungee extends Plugin {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<String> getLocalPlayers() {
|
final Set<UUID> getLocalPlayers() {
|
||||||
ImmutableSet.Builder<String> setBuilder = ImmutableSet.builder();
|
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
|
||||||
for (ProxiedPlayer pp : getProxy().getPlayers())
|
for (ProxiedPlayer pp : getProxy().getPlayers())
|
||||||
setBuilder = setBuilder.add(pp.getName());
|
setBuilder = setBuilder.add(pp.getUniqueId());
|
||||||
return setBuilder.build();
|
return setBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<String> getPlayers() {
|
final Set<String> getLocalPlayersAsUuidStrings() {
|
||||||
ImmutableSet.Builder<String> setBuilder = ImmutableSet.<String>builder().addAll(getLocalPlayers());
|
ImmutableSet.Builder<String> setBuilder = ImmutableSet.builder();
|
||||||
|
for (ProxiedPlayer pp : getProxy().getPlayers())
|
||||||
|
setBuilder = setBuilder.add(pp.getUniqueId().toString());
|
||||||
|
return setBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<UUID> getPlayers() {
|
||||||
|
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.<UUID>builder().addAll(getLocalPlayers());
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis rsc = pool.getResource();
|
Jedis rsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
for (String i : getServerIds()) {
|
for (String i : getServerIds()) {
|
||||||
if (i.equals(configuration.getString("server-id"))) continue;
|
if (i.equals(configuration.getString("server-id"))) continue;
|
||||||
Set<String> users = rsc.smembers("server:" + i + ":usersOnline");
|
Set<String> users = rsc.smembers("server:" + i + ":usersOnline");
|
||||||
if (users != null && !users.isEmpty())
|
if (users != null && !users.isEmpty()) {
|
||||||
setBuilder = setBuilder.addAll(users);
|
for (String user : users) {
|
||||||
|
if (UUIDTranslator.UUID_PATTERN.matcher(user).find()) {
|
||||||
|
setBuilder = setBuilder.add(UUID.fromString(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
// Redis server has disappeared!
|
// Redis server has disappeared!
|
||||||
@ -170,24 +191,24 @@ public final class RedisBungee extends Plugin {
|
|||||||
return setBuilder.build();
|
return setBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<String> getPlayersOnServer(@NonNull String server) {
|
final Set<UUID> getPlayersOnServer(@NonNull String server) {
|
||||||
checkArgument(getProxy().getServerInfo(server) != null, "server doesn't exist");
|
checkArgument(getProxy().getServerInfo(server) != null, "server doesn't exist");
|
||||||
return ImmutableSet.copyOf(serversToPlayers().get(server));
|
return ImmutableSet.copyOf(serversToPlayers().get(server));
|
||||||
}
|
}
|
||||||
|
|
||||||
final ServerInfo getServerFor(@NonNull String name) {
|
final ServerInfo getServerFor(@NonNull UUID uuid) {
|
||||||
ServerInfo server = null;
|
ServerInfo server = null;
|
||||||
if (getProxy().getPlayer(name) != null) return getProxy().getPlayer(name).getServer().getInfo();
|
if (getProxy().getPlayer(uuid) != null) return getProxy().getPlayer(uuid).getServer().getInfo();
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
if (tmpRsc.hexists("player:" + name, "server"))
|
if (tmpRsc.hexists("player:" + uuid, "server"))
|
||||||
server = getProxy().getServerInfo(tmpRsc.hget("player:" + name, "server"));
|
server = getProxy().getServerInfo(tmpRsc.hget("player:" + uuid, "server"));
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
// Redis server has disappeared!
|
// Redis server has disappeared!
|
||||||
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
||||||
pool.returnBrokenResource(tmpRsc);
|
pool.returnBrokenResource(tmpRsc);
|
||||||
throw new RuntimeException("Unable to get server for " + name, e);
|
throw new RuntimeException("Unable to get server for " + uuid, e);
|
||||||
} finally {
|
} finally {
|
||||||
pool.returnResource(tmpRsc);
|
pool.returnResource(tmpRsc);
|
||||||
}
|
}
|
||||||
@ -195,21 +216,21 @@ public final class RedisBungee extends Plugin {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long getLastOnline(@NonNull String name) {
|
final long getLastOnline(@NonNull UUID uuid) {
|
||||||
long time = -1L;
|
long time = -1L;
|
||||||
if (getProxy().getPlayer(name) != null) return 0;
|
if (getProxy().getPlayer(uuid) != null) return 0;
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
if (tmpRsc.hexists("player:" + name, "online"))
|
if (tmpRsc.hexists("player:" + uuid, "online"))
|
||||||
try {
|
try {
|
||||||
time = Long.valueOf(tmpRsc.hget("player:" + name, "online"));
|
time = Long.valueOf(tmpRsc.hget("player:" + uuid, "online"));
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
getLogger().info("I found a funny number for when " + name + " was last online!");
|
getLogger().info("I found a funny number for when " + uuid + " was last online!");
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (String proxyId : getServerIds()) {
|
for (String proxyId : getServerIds()) {
|
||||||
if (proxyId.equals(configuration.getString("server-id"))) continue;
|
if (proxyId.equals(configuration.getString("server-id"))) continue;
|
||||||
if (tmpRsc.sismember("server:" + proxyId + ":usersOnline", name)) {
|
if (tmpRsc.sismember("server:" + proxyId + ":usersOnline", uuid.toString())) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -217,18 +238,18 @@ public final class RedisBungee extends Plugin {
|
|||||||
String value = "0";
|
String value = "0";
|
||||||
if (!found) {
|
if (!found) {
|
||||||
value = String.valueOf(System.currentTimeMillis());
|
value = String.valueOf(System.currentTimeMillis());
|
||||||
getLogger().info(name + " isn't online. Setting to current time.");
|
getLogger().info(uuid + " isn't online. Setting to current time.");
|
||||||
} else {
|
} else {
|
||||||
getLogger().info(name + " is online. Setting to 0. Please check your BungeeCord instances.");
|
getLogger().info(uuid + " is online. Setting to 0. Please check your BungeeCord instances.");
|
||||||
getLogger().info("If they are working properly, and this error does not resolve in a few minutes, please let Tux know!");
|
getLogger().info("If they are working properly, and this error does not resolve in a few minutes, please let Tux know!");
|
||||||
}
|
}
|
||||||
tmpRsc.hset("player:" + name, "online", value);
|
tmpRsc.hset("player:" + uuid, "online", value);
|
||||||
}
|
}
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
// Redis server has disappeared!
|
// Redis server has disappeared!
|
||||||
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
||||||
pool.returnBrokenResource(tmpRsc);
|
pool.returnBrokenResource(tmpRsc);
|
||||||
throw new RuntimeException("Unable to get last time online for " + name, e);
|
throw new RuntimeException("Unable to get last time online for " + uuid, e);
|
||||||
} finally {
|
} finally {
|
||||||
pool.returnResource(tmpRsc);
|
pool.returnResource(tmpRsc);
|
||||||
}
|
}
|
||||||
@ -236,20 +257,20 @@ public final class RedisBungee extends Plugin {
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
final InetAddress getIpAddress(@NonNull String name) {
|
final InetAddress getIpAddress(@NonNull UUID uuid) {
|
||||||
if (getProxy().getPlayer(name) != null)
|
if (getProxy().getPlayer(uuid) != null)
|
||||||
return getProxy().getPlayer(name).getAddress().getAddress();
|
return getProxy().getPlayer(uuid).getAddress().getAddress();
|
||||||
InetAddress ia = null;
|
InetAddress ia = null;
|
||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
if (tmpRsc.hexists("player:" + name, "ip"))
|
if (tmpRsc.hexists("player:" + uuid, "ip"))
|
||||||
ia = InetAddress.getByName(tmpRsc.hget("player:" + name, "ip"));
|
ia = InetAddress.getByName(tmpRsc.hget("player:" + uuid, "ip"));
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
// Redis server has disappeared!
|
// Redis server has disappeared!
|
||||||
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
|
||||||
pool.returnBrokenResource(tmpRsc);
|
pool.returnBrokenResource(tmpRsc);
|
||||||
throw new RuntimeException("Unable to fetch IP address for " + name, e);
|
throw new RuntimeException("Unable to fetch IP address for " + uuid, e);
|
||||||
} catch (UnknownHostException ignored) {
|
} catch (UnknownHostException ignored) {
|
||||||
// Best to just return null
|
// Best to just return null
|
||||||
} finally {
|
} finally {
|
||||||
@ -286,36 +307,20 @@ public final class RedisBungee extends Plugin {
|
|||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
tmpRsc.set("server:" + configuration.getString("server-id") + ":playerCount", "0"); // reset
|
tmpRsc.hset("playerCounts", configuration.getString("server-id"), "0"); // reset
|
||||||
tmpRsc.hset("heartbeats", configuration.getString("server-id"), String.valueOf(System.currentTimeMillis()));
|
tmpRsc.hset("heartbeats", configuration.getString("server-id"), String.valueOf(System.currentTimeMillis()));
|
||||||
if (tmpRsc.scard("server:" + configuration.getString("server-id") + ":usersOnline") > 0) {
|
|
||||||
for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline")) {
|
|
||||||
// Are they simply on a different proxy?
|
|
||||||
boolean found = false;
|
|
||||||
for (String proxyId : tmpRsc.smembers("servers")) {
|
|
||||||
if (proxyId.equals(configuration.getString("server-id"))) continue;
|
|
||||||
if (tmpRsc.sismember("server:" + proxyId + ":usersOnline", member)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
RedisUtil.cleanUpPlayer(member, tmpRsc);
|
|
||||||
else
|
|
||||||
tmpRsc.srem("server:" + configuration.getString("server-id") + ":usersOnline", member);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
pool.returnResource(tmpRsc);
|
pool.returnResource(tmpRsc);
|
||||||
}
|
}
|
||||||
globalCount = getCurrentCount();
|
|
||||||
serverIds = getCurrentServerIds();
|
serverIds = getCurrentServerIds();
|
||||||
|
globalCount = getCurrentCount();
|
||||||
|
uuidTranslator = new UUIDTranslator(this);
|
||||||
getProxy().getScheduler().schedule(this, new Runnable() {
|
getProxy().getScheduler().schedule(this, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Jedis rsc = pool.getResource();
|
Jedis rsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
rsc.set("server:" + configuration.getString("server-id") + ":playerCount", String.valueOf(getProxy().getOnlineCount()));
|
rsc.hset("playerCounts", configuration.getString("server-id"), String.valueOf(getProxy().getOnlineCount()));
|
||||||
rsc.hset("heartbeats", configuration.getString("server-id"), String.valueOf(System.currentTimeMillis()));
|
rsc.hset("heartbeats", configuration.getString("server-id"), String.valueOf(System.currentTimeMillis()));
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
// Redis server has disappeared!
|
// Redis server has disappeared!
|
||||||
@ -324,16 +329,19 @@ public final class RedisBungee extends Plugin {
|
|||||||
} finally {
|
} finally {
|
||||||
pool.returnResource(rsc);
|
pool.returnResource(rsc);
|
||||||
}
|
}
|
||||||
globalCount = getCurrentCount();
|
|
||||||
serverIds = getCurrentServerIds();
|
serverIds = getCurrentServerIds();
|
||||||
|
globalCount = getCurrentCount();
|
||||||
}
|
}
|
||||||
}, 0, 3, TimeUnit.SECONDS);
|
}, 0, 3, TimeUnit.SECONDS);
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand());
|
consumer = new RedisBungeeConsumer(this);
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.FindCommand());
|
new Thread(consumer, "RedisBungee Consumer Thread").start();
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.LastSeenCommand());
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand(this));
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand());
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.FindCommand(this));
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll());
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.LastSeenCommand(this));
|
||||||
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId());
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand(this));
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll(this));
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId(this));
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerIds());
|
||||||
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this));
|
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this));
|
||||||
api = new RedisBungeeAPI(this);
|
api = new RedisBungeeAPI(this);
|
||||||
psl = new PubSubListener();
|
psl = new PubSubListener();
|
||||||
@ -343,7 +351,7 @@ public final class RedisBungee extends Plugin {
|
|||||||
public void run() {
|
public void run() {
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
Set<String> players = getLocalPlayers();
|
Set<String> players = getLocalPlayersAsUuidStrings();
|
||||||
for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline"))
|
for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline"))
|
||||||
if (!players.contains(member)) {
|
if (!players.contains(member)) {
|
||||||
// Are they simply on a different proxy?
|
// Are they simply on a different proxy?
|
||||||
@ -368,7 +376,7 @@ public final class RedisBungee extends Plugin {
|
|||||||
pool.returnResource(tmpRsc);
|
pool.returnResource(tmpRsc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1, 3, TimeUnit.MINUTES);
|
}, 0, 3, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
getProxy().registerChannel("RedisBungee");
|
getProxy().registerChannel("RedisBungee");
|
||||||
}
|
}
|
||||||
@ -378,6 +386,8 @@ public final class RedisBungee extends Plugin {
|
|||||||
if (pool != null) {
|
if (pool != null) {
|
||||||
// Poison the PubSub listener
|
// Poison the PubSub listener
|
||||||
getProxy().getScheduler().cancel(this);
|
getProxy().getScheduler().cancel(this);
|
||||||
|
getLogger().info("Waiting for consumer to finish writing data...");
|
||||||
|
consumer.stop();
|
||||||
Jedis tmpRsc = pool.getResource();
|
Jedis tmpRsc = pool.getResource();
|
||||||
try {
|
try {
|
||||||
tmpRsc.set("server:" + configuration.getString("server-id") + ":playerCount", "0"); // reset
|
tmpRsc.set("server:" + configuration.getString("server-id") + ":playerCount", "0"); // reset
|
||||||
@ -385,7 +395,7 @@ public final class RedisBungee extends Plugin {
|
|||||||
for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline"))
|
for (String member : tmpRsc.smembers("server:" + configuration.getString("server-id") + ":usersOnline"))
|
||||||
RedisUtil.cleanUpPlayer(member, tmpRsc);
|
RedisUtil.cleanUpPlayer(member, tmpRsc);
|
||||||
}
|
}
|
||||||
tmpRsc.srem("servers", configuration.getString("server-id"));
|
tmpRsc.hdel("heartbeats", configuration.getString("server-id"));
|
||||||
} finally {
|
} finally {
|
||||||
pool.returnResource(tmpRsc);
|
pool.returnResource(tmpRsc);
|
||||||
}
|
}
|
||||||
@ -437,13 +447,12 @@ public final class RedisBungee extends Plugin {
|
|||||||
File crashFile = new File(getDataFolder(), "restarted_from_crash.txt");
|
File crashFile = new File(getDataFolder(), "restarted_from_crash.txt");
|
||||||
if (crashFile.exists())
|
if (crashFile.exists())
|
||||||
crashFile.delete();
|
crashFile.delete();
|
||||||
else if (rsc.sismember("servers", configuration.getString("server-id"))) {
|
else if (rsc.hexists("heartbeat", configuration.getString("server-id"))) {
|
||||||
getLogger().severe("You have launched a possible imposter BungeeCord instance. Another instance is already running.");
|
getLogger().severe("You have launched a possible imposter BungeeCord instance. Another instance is already running.");
|
||||||
getLogger().severe("For data consistency reasons, RedisBungee will now disable itself.");
|
getLogger().severe("For data consistency reasons, RedisBungee will now disable itself.");
|
||||||
getLogger().severe("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.");
|
getLogger().severe("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.");
|
||||||
throw new RuntimeException("Possible imposter instance!");
|
throw new RuntimeException("Possible imposter instance!");
|
||||||
}
|
}
|
||||||
rsc.sadd("servers", configuration.getString("server-id"));
|
|
||||||
getLogger().log(Level.INFO, "Successfully connected to Redis.");
|
getLogger().log(Level.INFO, "Successfully connected to Redis.");
|
||||||
} catch (JedisConnectionException e) {
|
} catch (JedisConnectionException e) {
|
||||||
if (rsc != null)
|
if (rsc != null)
|
||||||
|
@ -13,6 +13,7 @@ import net.md_5.bungee.api.config.ServerInfo;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}.
|
* This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}.
|
||||||
@ -43,7 +44,7 @@ public class RedisBungeeAPI {
|
|||||||
* @param player a player name
|
* @param player a player name
|
||||||
* @return the last time a player was on, if online returns a 0
|
* @return the last time a player was on, if online returns a 0
|
||||||
*/
|
*/
|
||||||
public final long getLastOnline(@NonNull String player) {
|
public final long getLastOnline(@NonNull UUID player) {
|
||||||
return plugin.getLastOnline(player);
|
return plugin.getLastOnline(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,36 +55,38 @@ public class RedisBungeeAPI {
|
|||||||
* @param player a player name
|
* @param player a player name
|
||||||
* @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on.
|
* @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on.
|
||||||
*/
|
*/
|
||||||
public final ServerInfo getServerFor(@NonNull String player) {
|
public final ServerInfo getServerFor(@NonNull UUID player) {
|
||||||
return plugin.getServerFor(player);
|
return plugin.getServerFor(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a combined list of players on this network.
|
* Get a combined list of players on this network.
|
||||||
* <p>
|
* <p/>
|
||||||
* <strong>Note that this function returns an immutable {@link java.util.Set}.</strong>
|
* <strong>Note that this function returns an immutable {@link java.util.Set}.</strong>
|
||||||
*
|
*
|
||||||
* @return a Set with all players found
|
* @return a Set with all players found
|
||||||
*/
|
*/
|
||||||
public final Set<String> getPlayersOnline() {
|
public final Set<UUID> getPlayersOnline() {
|
||||||
return plugin.getPlayers();
|
return plugin.getPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a full list of players on all servers.
|
* Get a full list of players on all servers.
|
||||||
|
*
|
||||||
* @return a immutable Multimap with all players found on this server
|
* @return a immutable Multimap with all players found on this server
|
||||||
* @since 0.2.5
|
* @since 0.2.5
|
||||||
*/
|
*/
|
||||||
public final Multimap<String, String> getServerToPlayers() {
|
public final Multimap<String, UUID> getServerToPlayers() {
|
||||||
return plugin.serversToPlayers();
|
return plugin.serversToPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of players on the server with the given name.
|
* Get a list of players on the server with the given name.
|
||||||
|
*
|
||||||
* @param server a server name
|
* @param server a server name
|
||||||
* @return a Set with all players found on this server
|
* @return a Set with all players found on this server
|
||||||
*/
|
*/
|
||||||
public final Set<String> getPlayersOnServer(@NonNull String server) {
|
public final Set<UUID> getPlayersOnServer(@NonNull String server) {
|
||||||
return plugin.getPlayersOnServer(server);
|
return plugin.getPlayersOnServer(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +96,7 @@ public class RedisBungeeAPI {
|
|||||||
* @param player a player name
|
* @param player a player name
|
||||||
* @return if the player is online
|
* @return if the player is online
|
||||||
*/
|
*/
|
||||||
public final boolean isPlayerOnline(@NonNull String player) {
|
public final boolean isPlayerOnline(@NonNull UUID player) {
|
||||||
return getLastOnline(player) == 0;
|
return getLastOnline(player) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,12 +107,13 @@ public class RedisBungeeAPI {
|
|||||||
* @return an {@link java.net.InetAddress} if the player is online, null otherwise
|
* @return an {@link java.net.InetAddress} if the player is online, null otherwise
|
||||||
* @since 0.2.4
|
* @since 0.2.4
|
||||||
*/
|
*/
|
||||||
public final InetAddress getPlayerIp(@NonNull String player) {
|
public final InetAddress getPlayerIp(@NonNull UUID player) {
|
||||||
return plugin.getIpAddress(player);
|
return plugin.getIpAddress(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a proxy command to all proxies.
|
* Sends a proxy command to all proxies.
|
||||||
|
*
|
||||||
* @param command the command to send and execute
|
* @param command the command to send and execute
|
||||||
* @see #sendProxyCommand(String, String)
|
* @see #sendProxyCommand(String, String)
|
||||||
* @since 0.2.5
|
* @since 0.2.5
|
||||||
@ -120,6 +124,7 @@ public class RedisBungeeAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a proxy command to the proxy with the given ID. "allservers" means all proxies.
|
* Sends a proxy command to the proxy with the given ID. "allservers" means all proxies.
|
||||||
|
*
|
||||||
* @param proxyId a proxy ID
|
* @param proxyId a proxy ID
|
||||||
* @param command the command to send and execute
|
* @param command the command to send and execute
|
||||||
* @see #getServerId()
|
* @see #getServerId()
|
||||||
@ -132,9 +137,10 @@ public class RedisBungeeAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current BungeeCord server ID for this server.
|
* Get the current BungeeCord server ID for this server.
|
||||||
|
*
|
||||||
* @return the current server ID
|
* @return the current server ID
|
||||||
* @since 0.2.5
|
|
||||||
* @see #getAllServers()
|
* @see #getAllServers()
|
||||||
|
* @since 0.2.5
|
||||||
*/
|
*/
|
||||||
public final String getServerId() {
|
public final String getServerId() {
|
||||||
return RedisBungee.getConfiguration().getString("server-id");
|
return RedisBungee.getConfiguration().getString("server-id");
|
||||||
@ -142,9 +148,10 @@ public class RedisBungeeAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the linked proxies in this network.
|
* Get all the linked proxies in this network.
|
||||||
|
*
|
||||||
* @return the list of all proxies
|
* @return the list of all proxies
|
||||||
* @since 0.2.5
|
|
||||||
* @see #getServerId()
|
* @see #getServerId()
|
||||||
|
* @since 0.2.5
|
||||||
*/
|
*/
|
||||||
public final List<String> getAllServers() {
|
public final List<String> getAllServers() {
|
||||||
return plugin.getServerIds();
|
return plugin.getServerIds();
|
||||||
@ -169,4 +176,26 @@ public class RedisBungeeAPI {
|
|||||||
public final void unregisterPubSubChannels(String... channels) {
|
public final void unregisterPubSubChannels(String... channels) {
|
||||||
RedisBungee.getPubSubListener().removeChannel(channels);
|
RedisBungee.getPubSubListener().removeChannel(channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a name from the specified UUID.
|
||||||
|
*
|
||||||
|
* @param uuid the UUID to fetch the name for
|
||||||
|
* @return the name for the UUID
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
public final String getNameFromUuid(UUID uuid) {
|
||||||
|
return plugin.getUuidTranslator().getNameFromUuid(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a UUID from the specified name.
|
||||||
|
*
|
||||||
|
* @param name the UUID to fetch the name for
|
||||||
|
* @return the UUID for the name
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
public final UUID getUuidFromName(String name) {
|
||||||
|
return plugin.getUuidTranslator().getTranslatedUuid(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import java.util.Collections;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the CommandSender that RedisBungee uses to dispatch commands to BungeeCord.
|
* This class is the CommandSender that RedisBungee uses to dispatch commands to BungeeCord.
|
||||||
* <p>
|
* <p/>
|
||||||
* It inherits all permissions of the console command sender. Sending messages and modifying permissions are no-ops.
|
* It inherits all permissions of the console command sender. Sending messages and modifying permissions are no-ops.
|
||||||
*
|
*
|
||||||
* @author tuxed
|
* @author tuxed
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package com.imaginarycode.minecraft.redisbungee;
|
package com.imaginarycode.minecraft.redisbungee;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
@ -18,7 +19,9 @@ import net.md_5.bungee.api.plugin.Command;
|
|||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.TreeSet;
|
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.
|
* This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
|
||||||
@ -37,18 +40,28 @@ class RedisBungeeCommands {
|
|||||||
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
|
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
|
||||||
|
|
||||||
public static class GlistCommand extends Command {
|
public static class GlistCommand extends Command {
|
||||||
GlistCommand() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
GlistCommand(RedisBungee plugin) {
|
||||||
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
|
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args) {
|
public void execute(final CommandSender sender, final String[] args) {
|
||||||
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
int count = RedisBungee.getApi().getPlayerCount();
|
int count = RedisBungee.getApi().getPlayerCount();
|
||||||
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW).append(String.valueOf(count))
|
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW).append(String.valueOf(count))
|
||||||
.append(" player(s) are currently online.").create();
|
.append(" player(s) are currently online.").create();
|
||||||
if (args.length > 0 && args[0].equals("showall")) {
|
if (args.length > 0 && args[0].equals("showall")) {
|
||||||
if (RedisBungee.getConfiguration().getBoolean("canonical-glist", true)) {
|
if (RedisBungee.getConfiguration().getBoolean("canonical-glist", true)) {
|
||||||
Multimap<String, String> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
|
Multimap<String, UUID> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
|
||||||
|
Multimap<String, String> human = HashMultimap.create();
|
||||||
|
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
|
||||||
|
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue()));
|
||||||
|
}
|
||||||
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
|
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
|
||||||
TextComponent serverName = new TextComponent();
|
TextComponent serverName = new TextComponent();
|
||||||
serverName.setColor(ChatColor.GREEN);
|
serverName.setColor(ChatColor.GREEN);
|
||||||
@ -58,7 +71,7 @@ class RedisBungeeCommands {
|
|||||||
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
|
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
|
||||||
TextComponent serverPlayers = new TextComponent();
|
TextComponent serverPlayers = new TextComponent();
|
||||||
serverPlayers.setColor(ChatColor.WHITE);
|
serverPlayers.setColor(ChatColor.WHITE);
|
||||||
serverPlayers.setText(Joiner.on(", ").join(serverToPlayers.get(server)));
|
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
|
||||||
sender.sendMessage(serverName, serverCount, serverPlayers);
|
sender.sendMessage(serverName, serverCount, serverPlayers);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -71,17 +84,25 @@ class RedisBungeeCommands {
|
|||||||
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
|
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FindCommand extends Command {
|
public static class FindCommand extends Command {
|
||||||
FindCommand() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
FindCommand(RedisBungee plugin) {
|
||||||
super("find", "bungeecord.command.find", "rfind");
|
super("find", "bungeecord.command.find", "rfind");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args) {
|
public void execute(final CommandSender sender, final String[] args) {
|
||||||
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
ServerInfo si = RedisBungee.getApi().getServerFor(args[0]);
|
ServerInfo si = RedisBungee.getApi().getServerFor(plugin.getUuidTranslator().getTranslatedUuid(args[0]));
|
||||||
if (si != null) {
|
if (si != null) {
|
||||||
TextComponent message = new TextComponent();
|
TextComponent message = new TextComponent();
|
||||||
message.setColor(ChatColor.BLUE);
|
message.setColor(ChatColor.BLUE);
|
||||||
@ -94,17 +115,25 @@ class RedisBungeeCommands {
|
|||||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LastSeenCommand extends Command {
|
public static class LastSeenCommand extends Command {
|
||||||
LastSeenCommand() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
LastSeenCommand(RedisBungee plugin) {
|
||||||
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
|
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args) {
|
public void execute(final CommandSender sender, final String[] args) {
|
||||||
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
long secs = RedisBungee.getApi().getLastOnline(args[0]);
|
long secs = RedisBungee.getApi().getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(args[0]));
|
||||||
TextComponent message = new TextComponent();
|
TextComponent message = new TextComponent();
|
||||||
if (secs == 0) {
|
if (secs == 0) {
|
||||||
message.setColor(ChatColor.GREEN);
|
message.setColor(ChatColor.GREEN);
|
||||||
@ -123,17 +152,25 @@ class RedisBungeeCommands {
|
|||||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IpCommand extends Command {
|
public static class IpCommand extends Command {
|
||||||
IpCommand() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
IpCommand(RedisBungee plugin) {
|
||||||
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
|
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args) {
|
public void execute(final CommandSender sender, final String[] args) {
|
||||||
|
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
InetAddress ia = RedisBungee.getApi().getPlayerIp(args[0]);
|
InetAddress ia = RedisBungee.getApi().getPlayerIp(plugin.getUuidTranslator().getTranslatedUuid(args[0]));
|
||||||
if (ia != null) {
|
if (ia != null) {
|
||||||
TextComponent message = new TextComponent();
|
TextComponent message = new TextComponent();
|
||||||
message.setColor(ChatColor.GREEN);
|
message.setColor(ChatColor.GREEN);
|
||||||
@ -145,11 +182,16 @@ class RedisBungeeCommands {
|
|||||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SendToAll extends Command {
|
public static class SendToAll extends Command {
|
||||||
SendToAll() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
SendToAll(RedisBungee plugin) {
|
||||||
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
|
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -167,8 +209,11 @@ class RedisBungeeCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ServerId extends Command {
|
public static class ServerId extends Command {
|
||||||
ServerId() {
|
private final RedisBungee plugin;
|
||||||
|
|
||||||
|
ServerId(RedisBungee plugin) {
|
||||||
super("serverid", "redisbungee.command.serverid", "rserverid");
|
super("serverid", "redisbungee.command.serverid", "rserverid");
|
||||||
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -179,4 +224,18 @@ class RedisBungeeCommands {
|
|||||||
sender.sendMessage(textComponent);
|
sender.sendMessage(textComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ServerIds extends Command {
|
||||||
|
public ServerIds() {
|
||||||
|
super("serverids", "redisbungee.command.serverids");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSender sender, String[] strings) {
|
||||||
|
TextComponent textComponent = new TextComponent();
|
||||||
|
textComponent.setText("All server IDs: " + Joiner.on(", ").join(RedisBungee.getApi().getAllServers()));
|
||||||
|
textComponent.setColor(ChatColor.YELLOW);
|
||||||
|
sender.sendMessage(textComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee;
|
||||||
|
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.ConsumerEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerChangedServerConsumerEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerLoggedInConsumerEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerLoggedOffConsumerEvent;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RedisBungeeConsumer implements Runnable {
|
||||||
|
private final RedisBungee plugin;
|
||||||
|
private BlockingQueue<ConsumerEvent> consumerQueue = new LinkedBlockingQueue<>();
|
||||||
|
private boolean stopped = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Jedis jedis = plugin.getPool().getResource();
|
||||||
|
try {
|
||||||
|
while (!stopped) {
|
||||||
|
ConsumerEvent event;
|
||||||
|
try {
|
||||||
|
event = consumerQueue.take();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
handle(event, jedis);
|
||||||
|
}
|
||||||
|
for (ConsumerEvent event : consumerQueue)
|
||||||
|
handle(event, jedis);
|
||||||
|
consumerQueue.clear();
|
||||||
|
} finally {
|
||||||
|
plugin.getPool().returnResource(jedis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handle(ConsumerEvent event, Jedis jedis) {
|
||||||
|
if (event instanceof PlayerLoggedInConsumerEvent) {
|
||||||
|
PlayerLoggedInConsumerEvent event1 = (PlayerLoggedInConsumerEvent) event;
|
||||||
|
jedis.sadd("server:" + RedisBungee.getConfiguration().getString("server-id", "") + ":usersOnline", event1.getPlayer().getUniqueId().toString());
|
||||||
|
jedis.hset("player:" + event1.getPlayer().getUniqueId().toString(), "online", "0");
|
||||||
|
jedis.hset("player:" + event1.getPlayer().getUniqueId().toString(), "ip", event1.getPlayer().getAddress().getAddress().getHostAddress());
|
||||||
|
jedis.hset("player:" + event1.getPlayer().getUniqueId().toString(), "name", event1.getPlayer().getName());
|
||||||
|
jedis.hset("uuids", event1.getPlayer().getName(), event1.getPlayer().getUniqueId().toString());
|
||||||
|
} else if (event instanceof PlayerLoggedOffConsumerEvent) {
|
||||||
|
PlayerLoggedOffConsumerEvent event1 = (PlayerLoggedOffConsumerEvent) event;
|
||||||
|
jedis.hset("player:" + event1.getPlayer().getUniqueId().toString(), "online", String.valueOf(System.currentTimeMillis()));
|
||||||
|
RedisUtil.cleanUpPlayer(event1.getPlayer().getUniqueId().toString(), jedis);
|
||||||
|
} else if (event instanceof PlayerChangedServerConsumerEvent) {
|
||||||
|
PlayerChangedServerConsumerEvent event1 = (PlayerChangedServerConsumerEvent) event;
|
||||||
|
jedis.hset("player:" + event1.getPlayer().getUniqueId().toString(), "server", event1.getNewServer().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queue(ConsumerEvent event) {
|
||||||
|
if (!stopped)
|
||||||
|
consumerQueue.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
stopped = true;
|
||||||
|
while (!consumerQueue.isEmpty()) ;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
package com.imaginarycode.minecraft.redisbungee;
|
package com.imaginarycode.minecraft.redisbungee;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.io.ByteArrayDataInput;
|
import com.google.common.io.ByteArrayDataInput;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerChangedServerConsumerEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerLoggedInConsumerEvent;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.consumerevents.PlayerLoggedOffConsumerEvent;
|
||||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import net.md_5.bungee.api.ServerPing;
|
import net.md_5.bungee.api.ServerPing;
|
||||||
@ -14,7 +23,9 @@ import net.md_5.bungee.event.EventHandler;
|
|||||||
import redis.clients.jedis.Jedis;
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class RedisBungeeListener implements Listener {
|
public class RedisBungeeListener implements Listener {
|
||||||
@ -40,60 +51,17 @@ public class RedisBungeeListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerConnect(final PostLoginEvent event) {
|
public void onPlayerConnect(final PostLoginEvent event) {
|
||||||
if (plugin.getPool() != null) {
|
plugin.getConsumer().queue(new PlayerLoggedInConsumerEvent(event.getPlayer()));
|
||||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Jedis rsc = plugin.getPool().getResource();
|
|
||||||
try {
|
|
||||||
rsc.sadd("server:" + RedisBungee.getConfiguration().getString("server-id", "") + ":usersOnline", event.getPlayer().getName());
|
|
||||||
rsc.hset("player:" + event.getPlayer().getName(), "online", "0");
|
|
||||||
rsc.hset("player:" + event.getPlayer().getName(), "ip", event.getPlayer().getAddress().getAddress().getHostAddress());
|
|
||||||
rsc.hset("player:" + event.getPlayer().getName(), "name", event.getPlayer().getName());
|
|
||||||
} finally {
|
|
||||||
plugin.getPool().returnResource(rsc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// I used to have a task that eagerly waited for the user to be connected.
|
|
||||||
// Well, upon further inspection of BungeeCord's source code, this turned
|
|
||||||
// out to not be needed at all, since ServerConnectedEvent is called anyway.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerDisconnect(final PlayerDisconnectEvent event) {
|
public void onPlayerDisconnect(final PlayerDisconnectEvent event) {
|
||||||
if (plugin.getPool() != null) {
|
plugin.getConsumer().queue(new PlayerLoggedOffConsumerEvent(event.getPlayer()));
|
||||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Jedis rsc = plugin.getPool().getResource();
|
|
||||||
try {
|
|
||||||
rsc.hset("player:" + event.getPlayer().getName(), "online", String.valueOf(System.currentTimeMillis()));
|
|
||||||
RedisUtil.cleanUpPlayer(event.getPlayer().getName(), rsc);
|
|
||||||
} finally {
|
|
||||||
plugin.getPool().returnResource(rsc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onServerChange(final ServerConnectedEvent event) {
|
public void onServerChange(final ServerConnectedEvent event) {
|
||||||
if (plugin.getPool() != null) {
|
plugin.getConsumer().queue(new PlayerChangedServerConsumerEvent(event.getPlayer(), event.getServer().getInfo()));
|
||||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Jedis rsc = plugin.getPool().getResource();
|
|
||||||
try {
|
|
||||||
rsc.hset("player:" + event.getPlayer().getName(), "server", event.getServer().getInfo().getName());
|
|
||||||
} finally {
|
|
||||||
plugin.getPool().returnResource(rsc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -101,11 +69,11 @@ public class RedisBungeeListener implements Listener {
|
|||||||
ServerPing old = event.getResponse();
|
ServerPing old = event.getResponse();
|
||||||
ServerPing reply = new ServerPing();
|
ServerPing reply = new ServerPing();
|
||||||
if (RedisBungee.getConfiguration().getBoolean("player-list-in-ping", false)) {
|
if (RedisBungee.getConfiguration().getBoolean("player-list-in-ping", false)) {
|
||||||
Set<String> players = plugin.getPlayers();
|
Set<UUID> players = plugin.getPlayers();
|
||||||
ServerPing.PlayerInfo[] info = new ServerPing.PlayerInfo[players.size()];
|
ServerPing.PlayerInfo[] info = new ServerPing.PlayerInfo[players.size()];
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (String player : players) {
|
for (UUID player : players) {
|
||||||
info[idx] = new ServerPing.PlayerInfo(player, "");
|
info[idx] = new ServerPing.PlayerInfo(plugin.getUuidTranslator().getNameFromUuid(player), "");
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), players.size(), info));
|
reply.setPlayers(new ServerPing.Players(old.getPlayers().getMax(), players.size(), info));
|
||||||
@ -130,18 +98,21 @@ public class RedisBungeeListener implements Listener {
|
|||||||
switch (subchannel) {
|
switch (subchannel) {
|
||||||
case "PlayerList":
|
case "PlayerList":
|
||||||
out.writeUTF("Players");
|
out.writeUTF("Players");
|
||||||
Set<String> source = Collections.emptySet();
|
Set<UUID> original = Collections.emptySet();
|
||||||
type = in.readUTF();
|
type = in.readUTF();
|
||||||
if (type.equals("ALL")) {
|
if (type.equals("ALL")) {
|
||||||
out.writeUTF("ALL");
|
out.writeUTF("ALL");
|
||||||
source = plugin.getPlayers();
|
original = plugin.getPlayers();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
source = plugin.getPlayersOnServer(type);
|
original = plugin.getPlayersOnServer(type);
|
||||||
} catch (IllegalArgumentException ignored) {
|
} catch (IllegalArgumentException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.writeUTF(Joiner.on(',').join(source));
|
Set<String> players = new HashSet<>();
|
||||||
|
for (UUID uuid : original)
|
||||||
|
players.add(plugin.getUuidTranslator().getNameFromUuid(uuid));
|
||||||
|
out.writeUTF(Joiner.on(',').join(players));
|
||||||
break;
|
break;
|
||||||
case "PlayerCount":
|
case "PlayerCount":
|
||||||
out.writeUTF("PlayerCount");
|
out.writeUTF("PlayerCount");
|
||||||
@ -163,7 +134,7 @@ public class RedisBungeeListener implements Listener {
|
|||||||
String user = in.readUTF();
|
String user = in.readUTF();
|
||||||
out.writeUTF("LastOnline");
|
out.writeUTF("LastOnline");
|
||||||
out.writeUTF(user);
|
out.writeUTF(user);
|
||||||
out.writeLong(plugin.getLastOnline(user));
|
out.writeLong(plugin.getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(user)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
package com.imaginarycode.minecraft.redisbungee;
|
package com.imaginarycode.minecraft.redisbungee;
|
||||||
|
|
||||||
import redis.clients.jedis.Jedis;
|
import redis.clients.jedis.Jedis;
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.consumerevents;
|
||||||
|
|
||||||
|
public interface ConsumerEvent {
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.consumerevents;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class PlayerChangedServerConsumerEvent implements ConsumerEvent {
|
||||||
|
private final ProxiedPlayer player;
|
||||||
|
private final ServerInfo newServer;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.consumerevents;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class PlayerLoggedInConsumerEvent implements ConsumerEvent {
|
||||||
|
private final ProxiedPlayer player;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.consumerevents;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class PlayerLoggedOffConsumerEvent implements ConsumerEvent {
|
||||||
|
private final ProxiedPlayer player;
|
||||||
|
}
|
@ -11,7 +11,7 @@ import net.md_5.bungee.api.plugin.Event;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is posted when a PubSub message is received.
|
* This event is posted when a PubSub message is received.
|
||||||
*
|
* <p>
|
||||||
* <strong>Warning</strong>: This event is fired in a separate thread!
|
* <strong>Warning</strong>: This event is fired in a separate thread!
|
||||||
*
|
*
|
||||||
* @since 0.2.6
|
* @since 0.2.6
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
/* Credits to evilmidget38 for this class. I modified it to use Gson. */
|
||||||
|
public class NameFetcher implements Callable<Map<UUID, String>> {
|
||||||
|
private static final String PROFILE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/";
|
||||||
|
private final List<UUID> uuids;
|
||||||
|
|
||||||
|
public NameFetcher(List<UUID> uuids) {
|
||||||
|
this.uuids = ImmutableList.copyOf(uuids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<UUID, String> call() throws Exception {
|
||||||
|
Map<UUID, String> uuidStringMap = new HashMap<>();
|
||||||
|
for (UUID uuid : uuids) {
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) new URL(PROFILE_URL + uuid.toString().replace("-", "")).openConnection();
|
||||||
|
Map<String, String> response = RedisBungee.getGson().fromJson(new InputStreamReader(connection.getInputStream()), new TypeToken<Map<String, String>>() {
|
||||||
|
}.getType());
|
||||||
|
String name = response.get("name");
|
||||||
|
if (name == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String cause = response.get("cause");
|
||||||
|
String errorMessage = response.get("errorMessage");
|
||||||
|
if (cause != null && cause.length() > 0) {
|
||||||
|
throw new IllegalStateException(errorMessage);
|
||||||
|
}
|
||||||
|
uuidStringMap.put(uuid, name);
|
||||||
|
}
|
||||||
|
return uuidStringMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
/* Credits to evilmidget38 for this class. I modified it to use Gson. */
|
||||||
|
public class UUIDFetcher implements Callable<Map<String, UUID>> {
|
||||||
|
private static final double PROFILES_PER_REQUEST = 100;
|
||||||
|
private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
|
||||||
|
private final List<String> names;
|
||||||
|
private final boolean rateLimiting;
|
||||||
|
|
||||||
|
public UUIDFetcher(List<String> names, boolean rateLimiting) {
|
||||||
|
this.names = ImmutableList.copyOf(names);
|
||||||
|
this.rateLimiting = rateLimiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUIDFetcher(List<String> names) {
|
||||||
|
this(names, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, UUID> call() throws Exception {
|
||||||
|
Map<String, UUID> uuidMap = new HashMap<>();
|
||||||
|
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
|
||||||
|
for (int i = 0; i < requests; i++) {
|
||||||
|
HttpURLConnection connection = createConnection();
|
||||||
|
String body = RedisBungee.getGson().toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
|
||||||
|
writeBody(connection, body);
|
||||||
|
Profile[] array = RedisBungee.getGson().fromJson(new InputStreamReader(connection.getInputStream()), Profile[].class);
|
||||||
|
for (Profile profile : array) {
|
||||||
|
UUID uuid = UUIDFetcher.getUUID(profile.id);
|
||||||
|
uuidMap.put(profile.name, uuid);
|
||||||
|
}
|
||||||
|
if (rateLimiting && i != requests - 1) {
|
||||||
|
Thread.sleep(100L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uuidMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeBody(HttpURLConnection connection, String body) throws Exception {
|
||||||
|
OutputStream stream = connection.getOutputStream();
|
||||||
|
stream.write(body.getBytes());
|
||||||
|
stream.flush();
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpURLConnection createConnection() throws Exception {
|
||||||
|
URL url = new URL(PROFILE_URL);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UUID getUUID(String id) {
|
||||||
|
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(UUID uuid) {
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
|
||||||
|
byteBuffer.putLong(uuid.getMostSignificantBits());
|
||||||
|
byteBuffer.putLong(uuid.getLeastSignificantBits());
|
||||||
|
return byteBuffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID fromBytes(byte[] array) {
|
||||||
|
if (array.length != 16) {
|
||||||
|
throw new IllegalArgumentException("Illegal byte array length: " + array.length);
|
||||||
|
}
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
|
||||||
|
long mostSignificant = byteBuffer.getLong();
|
||||||
|
long leastSignificant = byteBuffer.getLong();
|
||||||
|
return new UUID(mostSignificant, leastSignificant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID getUUIDOf(String name) throws Exception {
|
||||||
|
return new UUIDFetcher(Collections.singletonList(name)).call().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Profile {
|
||||||
|
String id;
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2013 tuxed <write@imaginarycode.com>
|
||||||
|
* This work is free. You can redistribute it and/or modify it under the
|
||||||
|
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||||
|
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
|
||||||
|
*/
|
||||||
|
package com.imaginarycode.minecraft.redisbungee.util;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.collect.BiMap;
|
||||||
|
import com.google.common.collect.HashBiMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UUIDTranslator {
|
||||||
|
private final RedisBungee plugin;
|
||||||
|
private BiMap<String, UUID> uuidMap = Maps.synchronizedBiMap(HashBiMap.<String, UUID>create());
|
||||||
|
public static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");
|
||||||
|
|
||||||
|
public UUID getTranslatedUuid(String player) {
|
||||||
|
if (ProxyServer.getInstance().getPlayer(player) != null)
|
||||||
|
return ProxyServer.getInstance().getPlayer(player).getUniqueId();
|
||||||
|
|
||||||
|
UUID uuid = uuidMap.get(player);
|
||||||
|
if (uuid != null)
|
||||||
|
return uuid;
|
||||||
|
|
||||||
|
if (!plugin.getProxy().getConfig().isOnlineMode()) {
|
||||||
|
uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
|
||||||
|
uuidMap.put(player, uuid);
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay, it wasn't locally cached. Let's try Redis.
|
||||||
|
Jedis jedis = plugin.getPool().getResource();
|
||||||
|
try {
|
||||||
|
String stored = jedis.hget("uuids", player);
|
||||||
|
if (stored != null && UUID_PATTERN.matcher(stored).find()) {
|
||||||
|
// This is it!
|
||||||
|
uuid = UUID.fromString(stored);
|
||||||
|
uuidMap.put(stored, UUID.fromString(stored));
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// That didn't work. Let's ask Mojang.
|
||||||
|
uuid = UUIDFetcher.getUUIDOf(player);
|
||||||
|
|
||||||
|
if (uuid != null) {
|
||||||
|
uuidMap.put(stored, uuid);
|
||||||
|
jedis.hset("uuids", player, uuid.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuid;
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID for " + player, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
plugin.getPool().returnResource(jedis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNameFromUuid(UUID player) {
|
||||||
|
if (ProxyServer.getInstance().getPlayer(player) != null)
|
||||||
|
return ProxyServer.getInstance().getPlayer(player).getName();
|
||||||
|
|
||||||
|
String name = uuidMap.inverse().get(player);
|
||||||
|
|
||||||
|
if (name != null)
|
||||||
|
return name;
|
||||||
|
|
||||||
|
// Okay, it wasn't locally cached. Let's try Redis.
|
||||||
|
Jedis jedis = plugin.getPool().getResource();
|
||||||
|
try {
|
||||||
|
String stored = jedis.hget("player:" + player, "name");
|
||||||
|
if (stored != null) {
|
||||||
|
name = stored;
|
||||||
|
uuidMap.put(name, player);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// That didn't work. Let's ask Mojang.
|
||||||
|
name = new NameFetcher(Collections.singletonList(player)).call().get(player);
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
jedis.hset("player:" + player, "name", name);
|
||||||
|
uuidMap.put(name, player);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Unable to fetch name for " + player, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
plugin.getPool().returnResource(jedis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
name: RedisBungee
|
name: RedisBungee
|
||||||
main: com.imaginarycode.minecraft.redisbungee.RedisBungee
|
main: com.imaginarycode.minecraft.redisbungee.RedisBungee
|
||||||
version: 0.2.6
|
version: 0.3
|
||||||
author: tuxed
|
author: tuxed
|
||||||
# This is used so that we can automagically override default BungeeCord behavior.
|
# This is used so that we can automagically override default BungeeCord behavior.
|
||||||
softDepends: ["cmd_find", "cmd_list"]
|
softDepends: ["cmd_find", "cmd_list"]
|
Loading…
Reference in New Issue
Block a user