2013-09-29 20:16:47 +00:00
/ * *
* 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 ;
2014-05-23 03:37:26 +00:00
import com.google.common.base.Functions ;
import com.google.common.collect.* ;
2013-11-14 00:28:53 +00:00
import com.google.common.io.ByteStreams ;
2014-03-31 14:23:10 +00:00
import com.google.common.util.concurrent.ThreadFactoryBuilder ;
2014-04-20 05:12:28 +00:00
import com.google.gson.Gson ;
2014-03-31 15:19:33 +00:00
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent ;
2014-04-20 05:12:28 +00:00
import com.imaginarycode.minecraft.redisbungee.util.UUIDTranslator ;
2014-04-19 20:08:49 +00:00
import lombok.Getter ;
2014-01-02 05:14:58 +00:00
import lombok.NonNull ;
2013-09-29 20:16:47 +00:00
import net.md_5.bungee.api.config.ServerInfo ;
import net.md_5.bungee.api.connection.ProxiedPlayer ;
import net.md_5.bungee.api.plugin.Plugin ;
2013-12-24 04:12:54 +00:00
import net.md_5.bungee.config.Configuration ;
import net.md_5.bungee.config.ConfigurationProvider ;
import net.md_5.bungee.config.YamlConfiguration ;
2014-04-19 20:08:49 +00:00
import redis.clients.jedis.Jedis ;
import redis.clients.jedis.JedisPool ;
import redis.clients.jedis.JedisPoolConfig ;
import redis.clients.jedis.JedisPubSub ;
2013-12-04 21:54:51 +00:00
import redis.clients.jedis.exceptions.JedisConnectionException ;
2013-11-15 22:05:29 +00:00
import redis.clients.jedis.exceptions.JedisException ;
2013-09-29 20:16:47 +00:00
import java.io.* ;
2013-12-13 21:32:55 +00:00
import java.net.InetAddress ;
import java.net.UnknownHostException ;
2014-04-20 05:12:28 +00:00
import java.util.* ;
2014-03-31 14:23:10 +00:00
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Executors ;
2013-09-29 20:16:47 +00:00
import java.util.concurrent.TimeUnit ;
2013-12-04 21:54:51 +00:00
import java.util.logging.Level ;
2013-09-29 20:16:47 +00:00
2014-02-21 23:49:26 +00:00
import static com.google.common.base.Preconditions.checkArgument ;
2014-01-02 05:14:58 +00:00
2013-11-15 22:05:29 +00:00
/ * *
* The RedisBungee plugin .
2014-03-30 04:31:35 +00:00
* < p >
2013-11-15 22:05:29 +00:00
* The only function of interest is { @link # getApi ( ) } , which exposes some functions in this class .
* /
2014-04-19 20:08:49 +00:00
public final class RedisBungee extends Plugin {
2013-12-24 04:12:54 +00:00
private static Configuration configuration ;
2014-04-19 20:08:49 +00:00
@Getter
private JedisPool pool ;
2014-04-20 05:12:28 +00:00
@Getter
private RedisBungeeConsumer consumer ;
@Getter
private UUIDTranslator uuidTranslator ;
@Getter
private static Gson gson = new Gson ( ) ;
2014-05-20 12:30:43 +00:00
@Getter
2014-05-21 00:30:31 +00:00
private String serverId ;
2014-05-20 12:30:43 +00:00
2013-11-15 22:05:29 +00:00
private static RedisBungeeAPI api ;
2014-03-31 15:19:33 +00:00
private static PubSubListener psl = null ;
2014-04-19 19:54:30 +00:00
private List < String > serverIds ;
2014-03-30 04:31:35 +00:00
private int globalCount ;
2013-10-15 00:40:32 +00:00
2013-10-13 19:44:20 +00:00
/ * *
2013-11-15 22:05:29 +00:00
* Fetch the { @link RedisBungeeAPI } object created on plugin start .
2013-10-13 19:44:20 +00:00
*
2013-11-15 22:05:29 +00:00
* @return the { @link RedisBungeeAPI } object
2013-10-13 19:44:20 +00:00
* /
2013-11-15 22:05:29 +00:00
public static RedisBungeeAPI getApi ( ) {
return api ;
}
2013-12-24 05:15:06 +00:00
static Configuration getConfiguration ( ) {
2013-11-15 22:55:57 +00:00
return configuration ;
}
2014-04-04 03:03:27 +00:00
final List < String > getServerIds ( ) {
2014-04-19 19:54:30 +00:00
return serverIds ;
}
final List < String > getCurrentServerIds ( ) {
2014-04-04 03:03:27 +00:00
Jedis jedis = pool . getResource ( ) ;
try {
2014-04-19 19:52:15 +00:00
ImmutableList . Builder < String > servers = ImmutableList . builder ( ) ;
Map < String , String > heartbeats = jedis . hgetAll ( " heartbeats " ) ;
for ( Map . Entry < String , String > entry : heartbeats . entrySet ( ) ) {
try {
long stamp = Long . valueOf ( entry . getValue ( ) ) ;
2014-04-20 05:12:28 +00:00
if ( System . currentTimeMillis ( ) < stamp + 30000 )
2014-04-19 19:52:15 +00:00
servers . add ( entry . getKey ( ) ) ;
} catch ( NumberFormatException ignored ) {
}
}
return servers . build ( ) ;
2014-04-04 03:03:27 +00:00
} catch ( JedisConnectionException e ) {
getLogger ( ) . log ( Level . SEVERE , " Unable to fetch all server IDs " , e ) ;
2014-05-22 21:49:00 +00:00
pool . returnBrokenResource ( jedis ) ;
2014-05-20 12:30:43 +00:00
return Collections . singletonList ( serverId ) ;
2014-04-04 03:03:27 +00:00
} finally {
pool . returnResource ( jedis ) ;
}
2014-01-26 00:06:33 +00:00
}
2014-03-31 15:19:33 +00:00
static PubSubListener getPubSubListener ( ) {
return psl ;
}
2014-04-20 05:12:28 +00:00
final Multimap < String , UUID > serversToPlayers ( ) {
ImmutableMultimap . Builder < String , UUID > multimapBuilder = ImmutableMultimap . builder ( ) ;
for ( UUID p : getPlayers ( ) ) {
2014-01-02 05:14:58 +00:00
ServerInfo si = getServerFor ( p ) ;
2013-12-27 20:40:58 +00:00
if ( si ! = null )
2014-01-26 00:06:33 +00:00
multimapBuilder = multimapBuilder . put ( si . getName ( ) , p ) ;
2013-12-27 20:40:58 +00:00
}
2014-01-26 00:06:33 +00:00
return multimapBuilder . build ( ) ;
2013-12-27 20:40:58 +00:00
}
2013-12-24 05:15:06 +00:00
final int getCount ( ) {
2014-03-30 04:31:35 +00:00
return globalCount ;
}
final int getCurrentCount ( ) {
2014-01-09 21:15:15 +00:00
int c = getProxy ( ) . getOnlineCount ( ) ;
2013-12-04 21:54:51 +00:00
if ( pool ! = null ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-04-20 05:12:28 +00:00
List < String > serverIds = getServerIds ( ) ;
Map < String , String > counts = rsc . hgetAll ( " playerCounts " ) ;
for ( Map . Entry < String , String > entry : counts . entrySet ( ) ) {
if ( ! serverIds . contains ( entry . getKey ( ) ) )
continue ;
2014-05-20 12:30:43 +00:00
if ( entry . getKey ( ) . equals ( serverId ) ) continue ;
2014-04-20 05:12:28 +00:00
try {
c + = Integer . valueOf ( entry . getValue ( ) ) ;
} catch ( NumberFormatException e ) {
rsc . hset ( " playerCounts " , entry . getKey ( ) , " 0 " ) ;
}
2013-12-04 21:54:51 +00:00
}
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( rsc ) ;
throw new RuntimeException ( " Unable to get total player count " , e ) ;
2013-12-04 21:54:51 +00:00
} finally {
pool . returnResource ( rsc ) ;
2013-10-29 23:10:16 +00:00
}
}
return c ;
2013-10-15 00:40:32 +00:00
}
2014-04-20 05:12:28 +00:00
final Set < UUID > getLocalPlayers ( ) {
ImmutableSet . Builder < UUID > setBuilder = ImmutableSet . builder ( ) ;
for ( ProxiedPlayer pp : getProxy ( ) . getPlayers ( ) )
setBuilder = setBuilder . add ( pp . getUniqueId ( ) ) ;
return setBuilder . build ( ) ;
}
2014-05-23 03:37:26 +00:00
final Collection < String > getLocalPlayersAsUuidStrings ( ) {
return Collections2 . transform ( getLocalPlayers ( ) , Functions . toStringFunction ( ) ) ;
2014-01-20 15:34:40 +00:00
}
2014-04-20 05:12:28 +00:00
final Set < UUID > getPlayers ( ) {
ImmutableSet . Builder < UUID > setBuilder = ImmutableSet . < UUID > builder ( ) . addAll ( getLocalPlayers ( ) ) ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-04-26 23:43:09 +00:00
List < String > keys = new ArrayList < > ( ) ;
2014-04-19 19:54:30 +00:00
for ( String i : getServerIds ( ) ) {
2014-05-20 12:30:43 +00:00
if ( i . equals ( serverId ) )
2014-04-26 23:43:09 +00:00
continue ;
keys . add ( " server: " + i + " :usersOnline " ) ;
}
2014-04-27 00:09:53 +00:00
if ( ! keys . isEmpty ( ) ) {
Set < String > users = rsc . sunion ( keys . toArray ( new String [ keys . size ( ) ] ) ) ;
if ( users ! = null & & ! users . isEmpty ( ) ) {
for ( String user : users ) {
2014-05-23 03:37:26 +00:00
try {
2014-04-27 00:09:53 +00:00
setBuilder = setBuilder . add ( UUID . fromString ( user ) ) ;
2014-05-23 03:37:26 +00:00
} catch ( IllegalArgumentException ignored ) {
2014-04-27 00:09:53 +00:00
}
2014-04-20 05:12:28 +00:00
}
}
2013-09-29 20:16:47 +00:00
}
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( rsc ) ;
throw new RuntimeException ( " Unable to get all players online " , e ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( rsc ) ;
}
}
2014-01-26 00:06:33 +00:00
return setBuilder . build ( ) ;
2013-09-29 20:16:47 +00:00
}
2014-04-20 05:12:28 +00:00
final Set < UUID > getPlayersOnServer ( @NonNull String server ) {
2014-01-09 21:15:15 +00:00
checkArgument ( getProxy ( ) . getServerInfo ( server ) ! = null , " server doesn't exist " ) ;
2014-01-02 05:14:58 +00:00
return ImmutableSet . copyOf ( serversToPlayers ( ) . get ( server ) ) ;
2013-12-27 20:40:58 +00:00
}
2014-04-20 05:12:28 +00:00
final ServerInfo getServerFor ( @NonNull UUID uuid ) {
2013-09-29 20:16:47 +00:00
ServerInfo server = null ;
2014-04-20 05:12:28 +00:00
if ( getProxy ( ) . getPlayer ( uuid ) ! = null ) return getProxy ( ) . getPlayer ( uuid ) . getServer ( ) . getInfo ( ) ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
2014-04-20 05:12:28 +00:00
if ( tmpRsc . hexists ( " player: " + uuid , " server " ) )
server = getProxy ( ) . getServerInfo ( tmpRsc . hget ( " player: " + uuid , " server " ) ) ;
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( tmpRsc ) ;
2014-04-20 05:12:28 +00:00
throw new RuntimeException ( " Unable to get server for " + uuid , e ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
}
return server ;
}
2014-04-20 05:12:28 +00:00
final long getLastOnline ( @NonNull UUID uuid ) {
2013-11-14 01:25:09 +00:00
long time = - 1L ;
2014-04-20 05:12:28 +00:00
if ( getProxy ( ) . getPlayer ( uuid ) ! = null ) return 0 ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
2014-04-20 05:12:28 +00:00
if ( tmpRsc . hexists ( " player: " + uuid , " online " ) )
2014-01-26 00:06:33 +00:00
try {
2014-04-20 05:12:28 +00:00
time = Long . valueOf ( tmpRsc . hget ( " player: " + uuid , " online " ) ) ;
2014-01-26 00:06:33 +00:00
} catch ( NumberFormatException e ) {
2014-04-20 05:12:28 +00:00
getLogger ( ) . info ( " I found a funny number for when " + uuid + " was last online! " ) ;
2014-01-26 00:06:33 +00:00
boolean found = false ;
2014-04-04 03:03:27 +00:00
for ( String proxyId : getServerIds ( ) ) {
2014-05-20 12:30:43 +00:00
if ( proxyId . equals ( serverId ) ) continue ;
2014-04-20 05:12:28 +00:00
if ( tmpRsc . sismember ( " server: " + proxyId + " :usersOnline " , uuid . toString ( ) ) ) {
2014-01-26 00:06:33 +00:00
found = true ;
break ;
}
}
String value = " 0 " ;
if ( ! found ) {
value = String . valueOf ( System . currentTimeMillis ( ) ) ;
2014-04-20 05:12:28 +00:00
getLogger ( ) . info ( uuid + " isn't online. Setting to current time. " ) ;
2014-01-26 00:06:33 +00:00
} else {
2014-04-20 05:12:28 +00:00
getLogger ( ) . info ( uuid + " is online. Setting to 0. Please check your BungeeCord instances. " ) ;
2014-01-26 00:06:33 +00:00
getLogger ( ) . info ( " If they are working properly, and this error does not resolve in a few minutes, please let Tux know! " ) ;
}
2014-04-20 05:12:28 +00:00
tmpRsc . hset ( " player: " + uuid , " online " , value ) ;
2014-01-26 00:06:33 +00:00
}
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( tmpRsc ) ;
2014-04-20 05:12:28 +00:00
throw new RuntimeException ( " Unable to get last time online for " + uuid , e ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
}
return time ;
}
2014-04-20 05:12:28 +00:00
final InetAddress getIpAddress ( @NonNull UUID uuid ) {
if ( getProxy ( ) . getPlayer ( uuid ) ! = null )
return getProxy ( ) . getPlayer ( uuid ) . getAddress ( ) . getAddress ( ) ;
2013-12-13 21:32:55 +00:00
InetAddress ia = null ;
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
2014-04-20 05:12:28 +00:00
if ( tmpRsc . hexists ( " player: " + uuid , " ip " ) )
ia = InetAddress . getByName ( tmpRsc . hget ( " player: " + uuid , " ip " ) ) ;
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( tmpRsc ) ;
2014-04-20 05:12:28 +00:00
throw new RuntimeException ( " Unable to fetch IP address for " + uuid , e ) ;
2013-12-13 21:32:55 +00:00
} catch ( UnknownHostException ignored ) {
// Best to just return null
} finally {
pool . returnResource ( tmpRsc ) ;
}
}
return ia ;
}
2014-01-02 05:14:58 +00:00
final void sendProxyCommand ( @NonNull String proxyId , @NonNull String command ) {
2014-04-04 03:03:27 +00:00
checkArgument ( getServerIds ( ) . contains ( proxyId ) | | proxyId . equals ( " allservers " ) , " proxyId is invalid " ) ;
2014-01-02 00:05:55 +00:00
Jedis jedis = pool . getResource ( ) ;
try {
jedis . publish ( " redisbungee- " + proxyId , command ) ;
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( jedis ) ;
throw new RuntimeException ( " Unable to publish command " , e ) ;
2014-01-02 00:05:55 +00:00
} finally {
pool . returnResource ( jedis ) ;
}
}
2013-09-29 20:16:47 +00:00
@Override
public void onEnable ( ) {
try {
loadConfig ( ) ;
} catch ( IOException e ) {
2014-01-02 05:14:58 +00:00
throw new RuntimeException ( " Unable to load/save config " , e ) ;
2013-12-07 02:42:03 +00:00
} catch ( JedisConnectionException e ) {
throw new RuntimeException ( " Unable to connect to your Redis server! " , e ) ;
2013-09-29 20:16:47 +00:00
}
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
2014-05-20 12:30:43 +00:00
tmpRsc . hset ( " playerCounts " , serverId , " 0 " ) ; // reset
tmpRsc . hset ( " heartbeats " , serverId , String . valueOf ( System . currentTimeMillis ( ) ) ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
2014-04-19 19:54:30 +00:00
serverIds = getCurrentServerIds ( ) ;
2014-04-20 05:12:28 +00:00
globalCount = getCurrentCount ( ) ;
uuidTranslator = new UUIDTranslator ( this ) ;
2013-10-31 23:07:37 +00:00
getProxy ( ) . getScheduler ( ) . schedule ( this , new Runnable ( ) {
@Override
public void run ( ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-05-20 12:30:43 +00:00
rsc . hset ( " playerCounts " , serverId , String . valueOf ( getProxy ( ) . getOnlineCount ( ) ) ) ;
rsc . hset ( " heartbeats " , serverId , String . valueOf ( System . currentTimeMillis ( ) ) ) ;
2014-02-21 22:02:02 +00:00
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to update proxy counts - did your Redis server go away? " , e ) ;
pool . returnBrokenResource ( rsc ) ;
2013-10-31 23:07:37 +00:00
} finally {
pool . returnResource ( rsc ) ;
}
2014-04-19 19:54:30 +00:00
serverIds = getCurrentServerIds ( ) ;
2014-04-20 05:12:28 +00:00
globalCount = getCurrentCount ( ) ;
2013-10-31 23:07:37 +00:00
}
2014-03-30 04:31:35 +00:00
} , 0 , 3 , TimeUnit . SECONDS ) ;
2014-04-20 05:12:28 +00:00
consumer = new RedisBungeeConsumer ( this ) ;
new Thread ( consumer , " RedisBungee Consumer Thread " ) . start ( ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . GlistCommand ( this ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . FindCommand ( this ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . LastSeenCommand ( this ) ) ;
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 ( ) ) ;
2013-11-15 22:05:29 +00:00
api = new RedisBungeeAPI ( this ) ;
2014-05-21 00:32:43 +00:00
getProxy ( ) . getPluginManager ( ) . registerListener ( this , new RedisBungeeListener ( this ) ) ;
2013-11-15 22:05:29 +00:00
psl = new PubSubListener ( ) ;
2014-04-01 03:02:26 +00:00
getProxy ( ) . getScheduler ( ) . runAsync ( this , psl ) ;
2014-01-20 15:34:40 +00:00
getProxy ( ) . getScheduler ( ) . schedule ( this , new Runnable ( ) {
@Override
public void run ( ) {
2014-01-20 23:42:56 +00:00
Jedis tmpRsc = pool . getResource ( ) ;
2014-01-20 15:34:40 +00:00
try {
2014-05-23 03:37:26 +00:00
Collection < String > players = getLocalPlayersAsUuidStrings ( ) ;
2014-05-20 12:30:43 +00:00
for ( String member : tmpRsc . smembers ( " server: " + serverId + " :usersOnline " ) )
2014-01-20 23:42:56 +00:00
if ( ! players . contains ( member ) ) {
// Are they simply on a different proxy?
boolean found = false ;
2014-04-04 03:03:27 +00:00
for ( String proxyId : getServerIds ( ) ) {
2014-05-20 12:30:43 +00:00
if ( proxyId . equals ( serverId ) ) continue ;
2014-01-20 23:42:56 +00:00
if ( tmpRsc . sismember ( " server: " + proxyId + " :usersOnline " , member ) ) {
// Just clean up the set.
found = true ;
break ;
}
}
if ( ! found ) {
2014-04-19 20:08:49 +00:00
RedisUtil . cleanUpPlayer ( member , tmpRsc ) ;
2014-01-20 23:42:56 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally and globally: " + member ) ;
} else {
2014-05-20 12:30:43 +00:00
tmpRsc . srem ( " server: " + serverId + " :usersOnline " , member ) ;
2014-01-20 23:42:56 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally, but is on another proxy: " + member ) ;
}
}
} finally {
pool . returnResource ( tmpRsc ) ;
2014-01-20 15:34:40 +00:00
}
}
2014-04-20 05:12:28 +00:00
} , 0 , 3 , TimeUnit . MINUTES ) ;
2013-09-29 20:16:47 +00:00
}
2014-01-22 20:32:49 +00:00
getProxy ( ) . registerChannel ( " RedisBungee " ) ;
2013-09-29 20:16:47 +00:00
}
@Override
public void onDisable ( ) {
2013-10-18 21:14:58 +00:00
if ( pool ! = null ) {
2013-11-15 22:05:29 +00:00
// Poison the PubSub listener
getProxy ( ) . getScheduler ( ) . cancel ( this ) ;
2014-04-20 05:12:28 +00:00
getLogger ( ) . info ( " Waiting for consumer to finish writing data... " ) ;
consumer . stop ( ) ;
2013-10-18 21:14:58 +00:00
Jedis tmpRsc = pool . getResource ( ) ;
try {
2014-05-23 03:37:26 +00:00
tmpRsc . hdel ( " playerCounts " , serverId ) ;
2014-05-20 12:30:43 +00:00
if ( tmpRsc . scard ( " server: " + serverId + " :usersOnline " ) > 0 ) {
for ( String member : tmpRsc . smembers ( " server: " + serverId + " :usersOnline " ) )
2014-04-19 20:08:49 +00:00
RedisUtil . cleanUpPlayer ( member , tmpRsc ) ;
2013-12-03 21:01:27 +00:00
}
2014-05-20 12:30:43 +00:00
tmpRsc . hdel ( " heartbeats " , serverId ) ;
2013-10-18 21:14:58 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
2013-10-19 22:25:38 +00:00
pool . destroy ( ) ;
2013-10-18 21:14:58 +00:00
}
2013-09-29 20:16:47 +00:00
}
2013-12-07 02:42:03 +00:00
private void loadConfig ( ) throws IOException , JedisConnectionException {
2013-09-29 20:16:47 +00:00
if ( ! getDataFolder ( ) . exists ( ) ) {
getDataFolder ( ) . mkdir ( ) ;
}
2013-10-19 22:25:38 +00:00
File file = new File ( getDataFolder ( ) , " config.yml " ) ;
if ( ! file . exists ( ) ) {
file . createNewFile ( ) ;
try ( InputStream in = getResourceAsStream ( " example_config.yml " ) ;
OutputStream out = new FileOutputStream ( file ) ) {
2013-11-14 00:28:53 +00:00
ByteStreams . copy ( in , out ) ;
2013-09-29 20:16:47 +00:00
}
}
2013-12-24 04:12:54 +00:00
configuration = ConfigurationProvider . getProvider ( YamlConfiguration . class ) . load ( file ) ;
2013-10-19 22:25:38 +00:00
2013-12-24 04:12:54 +00:00
String redisServer = configuration . getString ( " redis-server " , " localhost " ) ;
int redisPort = configuration . getInt ( " redis-port " , 6379 ) ;
String redisPassword = configuration . getString ( " redis-password " ) ;
2014-05-20 12:30:43 +00:00
serverId = configuration . getString ( " server-id " ) ;
2013-09-29 20:16:47 +00:00
2013-12-24 04:12:54 +00:00
if ( redisPassword ! = null & & ( redisPassword . equals ( " " ) | | redisPassword . equals ( " none " ) ) ) {
redisPassword = null ;
2013-11-16 02:56:59 +00:00
}
2013-09-29 20:16:47 +00:00
2013-12-24 04:12:54 +00:00
// Configuration sanity checks.
2014-05-20 12:30:43 +00:00
if ( serverId = = null | | serverId . equals ( " " ) ) {
2013-12-24 04:12:54 +00:00
throw new RuntimeException ( " server-id is not specified in the configuration or is empty " ) ;
}
2013-11-15 22:55:57 +00:00
2013-09-29 20:16:47 +00:00
if ( redisServer ! = null ) {
if ( ! redisServer . equals ( " " ) ) {
2014-03-30 04:31:35 +00:00
JedisPoolConfig config = new JedisPoolConfig ( ) ;
config . setMaxTotal ( configuration . getInt ( " max-redis-connections " , - 1 ) ) ;
pool = new JedisPool ( config , redisServer , redisPort , 0 , redisPassword ) ;
2013-12-04 21:54:51 +00:00
// Test the connection
2013-12-07 02:27:28 +00:00
Jedis rsc = null ;
2013-12-04 21:54:51 +00:00
try {
2013-12-07 02:27:28 +00:00
rsc = pool . getResource ( ) ;
2013-12-04 21:54:51 +00:00
rsc . exists ( String . valueOf ( System . currentTimeMillis ( ) ) ) ;
2014-03-02 19:28:18 +00:00
// If that worked, now we can check for an existing, alive Bungee:
File crashFile = new File ( getDataFolder ( ) , " restarted_from_crash.txt " ) ;
if ( crashFile . exists ( ) )
crashFile . delete ( ) ;
2014-05-23 03:20:53 +00:00
else if ( rsc . hexists ( " heartbeats " , serverId ) ) {
2014-05-23 03:37:26 +00:00
try {
Long value = Long . valueOf ( rsc . hget ( " heartbeats " , serverId ) ) ;
if ( value ! = null & & System . currentTimeMillis ( ) < value + 20000 ) {
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 ( " 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! " ) ;
}
} catch ( NumberFormatException ignored ) {
}
2014-03-02 19:28:18 +00:00
}
2013-12-04 21:54:51 +00:00
getLogger ( ) . log ( Level . INFO , " Successfully connected to Redis. " ) ;
} catch ( JedisConnectionException e ) {
2013-12-07 02:27:28 +00:00
if ( rsc ! = null )
pool . returnBrokenResource ( rsc ) ;
2013-12-04 21:54:51 +00:00
pool . destroy ( ) ;
pool = null ;
rsc = null ;
2013-12-07 02:42:03 +00:00
throw e ;
2013-12-04 21:54:51 +00:00
} finally {
if ( rsc ! = null & & pool ! = null ) {
pool . returnResource ( rsc ) ;
}
}
2013-09-29 20:16:47 +00:00
}
2013-12-24 04:12:54 +00:00
} else {
throw new RuntimeException ( " No redis server specified! " ) ;
2013-09-29 20:16:47 +00:00
}
}
2014-03-31 15:19:33 +00:00
class PubSubListener implements Runnable {
2013-11-15 22:05:29 +00:00
private Jedis rsc ;
private JedisPubSubHandler jpsh ;
2014-01-20 15:34:40 +00:00
private PubSubListener ( ) {
}
2013-11-26 19:51:52 +00:00
2013-11-15 22:05:29 +00:00
@Override
public void run ( ) {
try {
rsc = pool . getResource ( ) ;
jpsh = new JedisPubSubHandler ( ) ;
2014-05-20 12:30:43 +00:00
rsc . subscribe ( jpsh , " redisbungee- " + serverId , " redisbungee-allservers " ) ;
2013-11-15 22:05:29 +00:00
} catch ( JedisException | ClassCastException ignored ) {
}
}
2014-03-31 15:19:33 +00:00
public void addChannel ( String . . . channel ) {
jpsh . subscribe ( channel ) ;
}
public void removeChannel ( String . . . channel ) {
jpsh . unsubscribe ( channel ) ;
}
2013-11-15 22:05:29 +00:00
}
2014-03-31 15:19:33 +00:00
class JedisPubSubHandler extends JedisPubSub {
private ExecutorService executor = Executors . newCachedThreadPool ( new ThreadFactoryBuilder ( ) . setDaemon ( true ) . setNameFormat ( " RedisBungee PubSub Handler - #%d " ) . build ( ) ) ;
2014-03-31 14:23:10 +00:00
2013-11-15 22:05:29 +00:00
@Override
2014-03-31 15:19:33 +00:00
public void onMessage ( final String s , final String s2 ) {
2014-01-26 00:06:33 +00:00
if ( s2 . trim ( ) . length ( ) = = 0 ) return ;
2014-03-31 14:23:10 +00:00
executor . submit ( new Runnable ( ) {
@Override
public void run ( ) {
2014-03-31 15:19:33 +00:00
getProxy ( ) . getPluginManager ( ) . callEvent ( new PubSubMessageEvent ( s , s2 ) ) ;
2014-03-31 14:23:10 +00:00
}
} ) ;
2013-11-15 22:05:29 +00:00
}
@Override
public void onPMessage ( String s , String s2 , String s3 ) {
}
@Override
public void onSubscribe ( String s , int i ) {
}
@Override
public void onUnsubscribe ( String s , int i ) {
}
@Override
public void onPUnsubscribe ( String s , int i ) {
}
@Override
public void onPSubscribe ( String s , int i ) {
}
}
2013-09-29 20:16:47 +00:00
}