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-01-09 21:15:15 +00:00
import com.google.common.base.Joiner ;
2014-02-21 23:49:26 +00:00
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableMultimap ;
import com.google.common.collect.ImmutableSet ;
import com.google.common.collect.Multimap ;
2014-01-26 00:06:33 +00:00
import com.google.common.io.ByteArrayDataInput ;
2014-01-09 21:15:15 +00:00
import com.google.common.io.ByteArrayDataOutput ;
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-03-31 15:19:33 +00:00
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent ;
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.ServerPing ;
import net.md_5.bungee.api.config.ServerInfo ;
import net.md_5.bungee.api.connection.ProxiedPlayer ;
2014-01-09 21:15:15 +00:00
import net.md_5.bungee.api.connection.Server ;
2013-12-03 20:39:36 +00:00
import net.md_5.bungee.api.event.* ;
2013-09-29 20:16:47 +00:00
import net.md_5.bungee.api.plugin.Listener ;
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 ;
2013-09-29 20:16:47 +00:00
import net.md_5.bungee.event.EventHandler ;
2013-12-03 21:01:27 +00:00
import redis.clients.jedis.* ;
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-02-21 23:49:26 +00:00
import java.util.Collections ;
import java.util.List ;
import java.util.Set ;
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 .
* /
2013-12-13 21:32:55 +00:00
public final class RedisBungee extends Plugin implements Listener {
2013-12-24 04:12:54 +00:00
private static Configuration configuration ;
2014-03-31 15:19:33 +00:00
private static JedisPool pool ;
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-01-26 00:06:33 +00:00
private static 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-01-26 00:06:33 +00:00
static List < String > getServerIds ( ) {
return serverIds ;
}
2014-03-31 15:19:33 +00:00
static PubSubListener getPubSubListener ( ) {
return psl ;
}
2014-01-02 05:14:58 +00:00
final Multimap < String , String > serversToPlayers ( ) {
2014-01-26 00:06:33 +00:00
ImmutableMultimap . Builder < String , String > multimapBuilder = ImmutableMultimap . builder ( ) ;
2014-01-02 05:14:58 +00:00
for ( String p : getPlayers ( ) ) {
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-01-26 00:06:33 +00:00
for ( String i : serverIds ) {
2013-12-24 04:12:54 +00:00
if ( i . equals ( configuration . getString ( " server-id " ) ) ) continue ;
2013-12-04 21:54:51 +00:00
if ( rsc . exists ( " server: " + i + " :playerCount " ) )
2014-01-26 00:06:33 +00:00
try {
c + = Integer . valueOf ( rsc . get ( " server: " + i + " :playerCount " ) ) ;
} catch ( NumberFormatException e ) {
getLogger ( ) . severe ( " I found a funny number for " + i + " 's player count. Resetting it to 0. " ) ;
rsc . set ( " server: " + i + " :playerCount " , " 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-01-20 15:34:40 +00:00
final Set < String > getLocalPlayers ( ) {
2014-01-26 00:06:33 +00:00
ImmutableSet . Builder < String > setBuilder = ImmutableSet . builder ( ) ;
2014-01-20 15:34:40 +00:00
for ( ProxiedPlayer pp : getProxy ( ) . getPlayers ( ) )
2014-01-26 00:06:33 +00:00
setBuilder = setBuilder . add ( pp . getName ( ) ) ;
return setBuilder . build ( ) ;
2014-01-20 15:34:40 +00:00
}
final Set < String > getPlayers ( ) {
2014-01-26 00:06:33 +00:00
ImmutableSet . Builder < String > setBuilder = ImmutableSet . < String > builder ( ) . addAll ( getLocalPlayers ( ) ) ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-01-26 00:06:33 +00:00
for ( String i : serverIds ) {
2013-12-24 04:12:54 +00:00
if ( i . equals ( configuration . getString ( " server-id " ) ) ) continue ;
2014-01-26 00:06:33 +00:00
Set < String > users = rsc . smembers ( " server: " + i + " :usersOnline " ) ;
if ( users ! = null & & ! users . isEmpty ( ) )
setBuilder = setBuilder . addAll ( users ) ;
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-01-02 05:14:58 +00:00
final Set < String > 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-01-02 05:14:58 +00:00
final ServerInfo getServerFor ( @NonNull String name ) {
2013-09-29 20:16:47 +00:00
ServerInfo server = null ;
2014-01-09 21:15:15 +00:00
if ( getProxy ( ) . getPlayer ( name ) ! = null ) return getProxy ( ) . getPlayer ( name ) . getServer ( ) . getInfo ( ) ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
if ( tmpRsc . hexists ( " player: " + name , " server " ) )
2014-01-09 21:15:15 +00:00
server = getProxy ( ) . getServerInfo ( tmpRsc . hget ( " player: " + name , " 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-02-21 23:49:26 +00:00
throw new RuntimeException ( " Unable to get server for " + name , e ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
}
return server ;
}
2014-01-02 05:14:58 +00:00
final long getLastOnline ( @NonNull String name ) {
2013-11-14 01:25:09 +00:00
long time = - 1L ;
2014-01-09 21:15:15 +00:00
if ( getProxy ( ) . getPlayer ( name ) ! = null ) return 0 ;
2013-09-29 20:16:47 +00:00
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
if ( tmpRsc . hexists ( " player: " + name , " online " ) )
2014-01-26 00:06:33 +00:00
try {
time = Long . valueOf ( tmpRsc . hget ( " player: " + name , " online " ) ) ;
} catch ( NumberFormatException e ) {
getLogger ( ) . info ( " I found a funny number for when " + name + " was last online! " ) ;
boolean found = false ;
for ( String proxyId : serverIds ) {
if ( proxyId . equals ( configuration . getString ( " server-id " ) ) ) continue ;
if ( tmpRsc . sismember ( " server: " + proxyId + " :usersOnline " , name ) ) {
found = true ;
break ;
}
}
String value = " 0 " ;
if ( ! found ) {
value = String . valueOf ( System . currentTimeMillis ( ) ) ;
getLogger ( ) . info ( name + " isn't online. Setting to current time. " ) ;
} else {
getLogger ( ) . info ( name + " 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! " ) ;
}
tmpRsc . hset ( " player: " + name , " online " , value ) ;
}
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 ) ;
throw new RuntimeException ( " Unable to get last time online for " + name , e ) ;
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
}
return time ;
}
2014-01-02 05:14:58 +00:00
final InetAddress getIpAddress ( @NonNull String name ) {
2014-01-09 21:15:15 +00:00
if ( getProxy ( ) . getPlayer ( name ) ! = null )
return getProxy ( ) . getPlayer ( name ) . getAddress ( ) . getAddress ( ) ;
2013-12-13 21:32:55 +00:00
InetAddress ia = null ;
if ( pool ! = null ) {
Jedis tmpRsc = pool . getResource ( ) ;
try {
if ( tmpRsc . hexists ( " player: " + name , " ip " ) )
ia = InetAddress . getByName ( tmpRsc . hget ( " player: " + name , " 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-02-21 23:49:26 +00:00
throw new RuntimeException ( " Unable to fetch IP address for " + name , 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-01-26 00:06:33 +00:00
checkArgument ( serverIds . 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 {
2013-12-24 04:12:54 +00:00
tmpRsc . set ( " server: " + configuration . getString ( " server-id " ) + " :playerCount " , " 0 " ) ; // reset
if ( tmpRsc . scard ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) > 0 ) {
2014-01-20 23:42:56 +00:00
for ( String member : tmpRsc . smembers ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) ) {
// Are they simply on a different proxy?
boolean found = false ;
2014-01-26 00:06:33 +00:00
for ( String proxyId : serverIds ) {
2014-01-20 23:42:56 +00:00
if ( proxyId . equals ( configuration . getString ( " server-id " ) ) ) continue ;
if ( tmpRsc . sismember ( " server: " + proxyId + " :usersOnline " , member ) ) {
found = true ;
break ;
}
}
if ( ! found )
cleanUpPlayer ( member , tmpRsc ) ;
else
tmpRsc . srem ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " , member ) ;
}
2013-12-03 21:01:27 +00:00
}
2013-09-29 20:16:47 +00:00
} finally {
pool . returnResource ( tmpRsc ) ;
}
2014-03-30 04:31:35 +00:00
globalCount = getCurrentCount ( ) ;
2013-10-31 23:07:37 +00:00
getProxy ( ) . getScheduler ( ) . schedule ( this , new Runnable ( ) {
@Override
public void run ( ) {
Jedis rsc = pool . getResource ( ) ;
try {
2013-12-27 20:40:58 +00:00
rsc . set ( " server: " + configuration . getString ( " server-id " ) + " :playerCount " , String . valueOf ( getProxy ( ) . getOnlineCount ( ) ) ) ;
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-03-30 04:31:35 +00:00
globalCount = getCurrentCount ( ) ;
2013-10-31 23:07:37 +00:00
}
2014-03-30 04:31:35 +00:00
} , 0 , 3 , TimeUnit . SECONDS ) ;
2013-11-15 22:55:57 +00:00
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . GlistCommand ( ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . FindCommand ( ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . LastSeenCommand ( ) ) ;
2013-12-13 21:32:55 +00:00
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . IpCommand ( ) ) ;
2014-01-17 00:49:57 +00:00
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . SendToAll ( ) ) ;
2014-01-17 02:31:57 +00:00
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . ServerId ( ) ) ;
2013-09-29 20:16:47 +00:00
getProxy ( ) . getPluginManager ( ) . registerListener ( this , this ) ;
2013-11-15 22:05:29 +00:00
api = new RedisBungeeAPI ( this ) ;
psl = new PubSubListener ( ) ;
2014-01-20 15:16:30 +00:00
new Thread ( psl , " RedisBungee PubSub Listener " ) . start ( ) ;
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-01-20 23:42:56 +00:00
Set < String > players = getLocalPlayers ( ) ;
for ( String member : tmpRsc . smembers ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) )
if ( ! players . contains ( member ) ) {
// Are they simply on a different proxy?
boolean found = false ;
2014-01-26 00:06:33 +00:00
for ( String proxyId : serverIds ) {
2014-01-20 23:42:56 +00:00
if ( proxyId . equals ( configuration . getString ( " server-id " ) ) ) continue ;
if ( tmpRsc . sismember ( " server: " + proxyId + " :usersOnline " , member ) ) {
// Just clean up the set.
found = true ;
break ;
}
}
if ( ! found ) {
cleanUpPlayer ( member , tmpRsc ) ;
getLogger ( ) . warning ( " Player found in set that was not found locally and globally: " + member ) ;
} else {
tmpRsc . srem ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " , member ) ;
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-01-26 00:06:33 +00:00
} , 1 , 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
psl . poison ( ) ;
getProxy ( ) . getScheduler ( ) . cancel ( this ) ;
2013-10-18 21:14:58 +00:00
Jedis tmpRsc = pool . getResource ( ) ;
try {
2013-12-24 04:12:54 +00:00
tmpRsc . set ( " server: " + configuration . getString ( " server-id " ) + " :playerCount " , " 0 " ) ; // reset
if ( tmpRsc . scard ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) > 0 ) {
for ( String member : tmpRsc . smembers ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) )
2013-12-18 17:10:32 +00:00
cleanUpPlayer ( member , tmpRsc ) ;
2013-12-03 21:01:27 +00:00
}
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 " ) ;
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-01-02 05:14:58 +00:00
if ( configuration . get ( " server-id " ) = = null | | configuration . getString ( " server-id " ) . 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
2014-01-02 05:14:58 +00:00
if ( configuration . getStringList ( " linked-servers " ) . isEmpty ( ) ) {
2013-12-24 04:12:54 +00:00
throw new RuntimeException ( " linked-servers is not specified in the configuration or is empty " ) ;
2013-12-03 21:01:27 +00:00
}
2014-01-26 00:06:33 +00:00
serverIds = ImmutableList . copyOf ( configuration . getStringList ( " linked-servers " ) ) ;
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 ( ) ;
else if ( rsc . exists ( " server: " + configuration . get ( " server-id " ) + " :playerCount " ) ) {
if ( Integer . valueOf ( rsc . get ( " server: " + configuration . get ( " server-id " ) + " :playerCount " ) ) > 0 & &
rsc . scard ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " ) > 0 ) {
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! " ) ;
}
}
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
}
}
2013-12-15 17:07:42 +00:00
@EventHandler
public void onPreLogin ( PreLoginEvent event ) {
if ( pool ! = null ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-01-26 00:06:33 +00:00
for ( String server : serverIds ) {
2014-03-31 14:23:10 +00:00
if ( rsc . sismember ( " server: " + server + " :usersOnline " , event . getConnection ( ) . getUUID ( ) ) ) {
2013-12-15 17:07:42 +00:00
event . setCancelled ( true ) ;
event . setCancelReason ( " You are already logged on to this server. " ) ;
}
}
} finally {
pool . returnResource ( rsc ) ;
}
}
}
2013-12-03 20:39:36 +00:00
@EventHandler
public void onPlayerConnect ( final PostLoginEvent event ) {
2013-12-04 21:54:51 +00:00
if ( pool ! = null ) {
2014-03-30 04:31:35 +00:00
getProxy ( ) . getScheduler ( ) . runAsync ( RedisBungee . this , new Runnable ( ) {
@Override
public void run ( ) {
Jedis rsc = pool . getResource ( ) ;
try {
rsc . sadd ( " server: " + configuration . getString ( " server-id " , " " ) + " :usersOnline " , event . getPlayer ( ) . getName ( ) ) ;
2014-03-31 14:23:10 +00:00
rsc . hset ( " player: " + event . getPlayer ( ) . getUUID ( ) , " online " , " 0 " ) ;
rsc . hset ( " player: " + event . getPlayer ( ) . getUUID ( ) , " ip " , event . getPlayer ( ) . getAddress ( ) . getAddress ( ) . getHostAddress ( ) ) ;
rsc . hset ( " player: " + event . getPlayer ( ) . getUUID ( ) , " name " , event . getPlayer ( ) . getName ( ) ) ;
2014-03-30 04:31:35 +00:00
} finally {
pool . returnResource ( rsc ) ;
}
}
} ) ;
2013-11-16 03:09:04 +00:00
}
// 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.
2013-09-29 20:16:47 +00:00
}
@EventHandler
2013-11-16 02:56:59 +00:00
public void onPlayerDisconnect ( final PlayerDisconnectEvent event ) {
2013-11-16 03:09:04 +00:00
if ( pool ! = null ) {
2014-03-30 04:31:35 +00:00
getProxy ( ) . getScheduler ( ) . runAsync ( RedisBungee . this , new Runnable ( ) {
@Override
public void run ( ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-03-31 14:23:10 +00:00
rsc . hset ( " player: " + event . getPlayer ( ) . getUUID ( ) , " online " , String . valueOf ( System . currentTimeMillis ( ) ) ) ;
cleanUpPlayer ( event . getPlayer ( ) . getUUID ( ) , rsc ) ;
2014-03-30 04:31:35 +00:00
} finally {
pool . returnResource ( rsc ) ;
}
}
} ) ;
2013-11-16 03:09:04 +00:00
}
2013-09-29 20:16:47 +00:00
}
@EventHandler
2013-11-16 02:56:59 +00:00
public void onServerChange ( final ServerConnectedEvent event ) {
2013-11-16 03:09:04 +00:00
if ( pool ! = null ) {
2014-03-30 04:31:35 +00:00
getProxy ( ) . getScheduler ( ) . runAsync ( RedisBungee . this , new Runnable ( ) {
@Override
public void run ( ) {
Jedis rsc = pool . getResource ( ) ;
try {
2014-03-31 14:23:10 +00:00
rsc . hset ( " player: " + event . getPlayer ( ) . getUUID ( ) , " server " , event . getServer ( ) . getInfo ( ) . getName ( ) ) ;
2014-03-30 04:31:35 +00:00
} finally {
pool . returnResource ( rsc ) ;
}
}
} ) ;
2013-11-16 03:09:04 +00:00
}
2013-09-29 20:16:47 +00:00
}
@EventHandler
public void onPing ( ProxyPingEvent event ) {
ServerPing old = event . getResponse ( ) ;
2013-10-29 02:42:26 +00:00
ServerPing reply = new ServerPing ( ) ;
2013-12-24 04:12:54 +00:00
if ( configuration . getBoolean ( " player-list-in-ping " , false ) ) {
2013-11-16 02:56:59 +00:00
Set < String > players = getPlayers ( ) ;
ServerPing . PlayerInfo [ ] info = new ServerPing . PlayerInfo [ players . size ( ) ] ;
int idx = 0 ;
for ( String player : players ) {
info [ idx ] = new ServerPing . PlayerInfo ( player , " " ) ;
idx + + ;
}
reply . setPlayers ( new ServerPing . Players ( old . getPlayers ( ) . getMax ( ) , players . size ( ) , info ) ) ;
} else {
2014-01-26 00:06:33 +00:00
reply . setPlayers ( new ServerPing . Players ( old . getPlayers ( ) . getMax ( ) , getCount ( ) , null ) ) ;
2013-11-16 02:56:59 +00:00
}
2013-10-29 02:42:26 +00:00
reply . setDescription ( old . getDescription ( ) ) ;
reply . setFavicon ( old . getFavicon ( ) ) ;
reply . setVersion ( old . getVersion ( ) ) ;
event . setResponse ( reply ) ;
2013-09-29 20:16:47 +00:00
}
2013-11-15 22:05:29 +00:00
2014-01-09 21:15:15 +00:00
@EventHandler
public void onPluginMessage ( PluginMessageEvent event ) {
if ( event . getTag ( ) . equals ( " RedisBungee " ) & & event . getSender ( ) instanceof Server ) {
2014-01-26 00:06:33 +00:00
ByteArrayDataInput in = ByteStreams . newDataInput ( event . getData ( ) ) ;
String subchannel = in . readUTF ( ) ;
ByteArrayDataOutput out = ByteStreams . newDataOutput ( ) ;
String type ;
switch ( subchannel ) {
case " PlayerList " :
out . writeUTF ( " Players " ) ;
Set < String > source = Collections . emptySet ( ) ;
type = in . readUTF ( ) ;
if ( type . equals ( " ALL " ) ) {
out . writeUTF ( " ALL " ) ;
source = getPlayers ( ) ;
} else {
try {
source = getPlayersOnServer ( type ) ;
} catch ( IllegalArgumentException ignored ) {
2014-01-09 21:15:15 +00:00
}
2014-01-26 00:06:33 +00:00
}
out . writeUTF ( Joiner . on ( ',' ) . join ( source ) ) ;
break ;
case " PlayerCount " :
out . writeUTF ( " PlayerCount " ) ;
type = in . readUTF ( ) ;
if ( type . equals ( " ALL " ) ) {
out . writeUTF ( " ALL " ) ;
2014-03-30 04:31:35 +00:00
out . writeInt ( getCurrentCount ( ) ) ;
2014-01-26 00:06:33 +00:00
} else {
out . writeUTF ( type ) ;
try {
out . writeInt ( getPlayersOnServer ( type ) . size ( ) ) ;
} catch ( IllegalArgumentException e ) {
out . writeInt ( 0 ) ;
}
}
2014-03-30 04:31:35 +00:00
out . writeInt ( getCurrentCount ( ) ) ;
2014-01-26 00:06:33 +00:00
break ;
case " LastOnline " :
String user = in . readUTF ( ) ;
out . writeUTF ( " LastOnline " ) ;
out . writeUTF ( user ) ;
out . writeLong ( getLastOnline ( user ) ) ;
break ;
default :
break ;
2014-01-09 21:15:15 +00:00
}
2014-01-26 00:06:33 +00:00
( ( Server ) event . getSender ( ) ) . sendData ( " RedisBungee " , out . toByteArray ( ) ) ;
2014-01-09 21:15:15 +00:00
}
}
2014-03-31 15:19:33 +00:00
@EventHandler
public void onPubSubMessage ( PubSubMessageEvent event ) {
if ( event . getChannel ( ) . equals ( " redisbungee-allservers " ) | | event . getChannel ( ) . equals ( " redisbungee- " + configuration . getString ( " server-id " ) ) ) {
String message = event . getMessage ( ) ;
if ( message . startsWith ( " / " ) )
message = message . substring ( 1 ) ;
getLogger ( ) . info ( " Invoking command via PubSub: / " + message ) ;
getProxy ( ) . getPluginManager ( ) . dispatchCommand ( RedisBungeeCommandSender . instance , message ) ;
}
}
2013-12-18 17:10:32 +00:00
private void cleanUpPlayer ( String player , Jedis rsc ) {
2013-12-24 04:12:54 +00:00
rsc . srem ( " server: " + configuration . getString ( " server-id " ) + " :usersOnline " , player ) ;
2013-12-18 17:10:32 +00:00
rsc . hdel ( " player: " + player , " server " ) ;
rsc . hdel ( " player: " + player , " ip " ) ;
}
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 ( ) ;
2013-12-24 04:12:54 +00:00
rsc . subscribe ( jpsh , " redisbungee- " + configuration . getString ( " server-id " ) , " redisbungee-allservers " ) ;
2013-11-15 22:05:29 +00:00
} catch ( JedisException | ClassCastException ignored ) {
}
}
public void poison ( ) {
jpsh . unsubscribe ( ) ;
pool . returnResource ( rsc ) ;
}
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
}