2022-04-13 18:17:38 +00:00
package com.imaginarycode.minecraft.redisbungee ;
import com.google.common.cache.Cache ;
import com.google.common.cache.CacheBuilder ;
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableMultimap ;
import com.google.common.collect.ImmutableSet ;
import com.google.common.collect.Multimap ;
2022-07-22 11:12:32 +00:00
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration ;
2022-07-20 09:21:24 +00:00
import com.imaginarycode.minecraft.redisbungee.api.tasks.HeartbeatTask ;
2022-07-17 03:41:18 +00:00
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask ;
import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil ;
2022-07-19 11:30:45 +00:00
import com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils ;
2022-04-13 21:56:17 +00:00
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands ;
2022-04-14 11:59:02 +00:00
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent ;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent ;
2022-07-20 08:32:04 +00:00
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent ;
2022-04-14 11:59:02 +00:00
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent ;
2022-07-17 03:41:18 +00:00
import com.imaginarycode.minecraft.redisbungee.api.* ;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner ;
import com.imaginarycode.minecraft.redisbungee.api.util.io.IOUtil ;
import com.imaginarycode.minecraft.redisbungee.api.util.lua.LuaManager ;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode ;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.NameFetcher ;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDFetcher ;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator ;
2022-04-13 18:17:38 +00:00
import com.squareup.okhttp.Dispatcher ;
import com.squareup.okhttp.OkHttpClient ;
import net.md_5.bungee.api.ProxyServer ;
import net.md_5.bungee.api.connection.ProxiedPlayer ;
import net.md_5.bungee.api.plugin.Event ;
import net.md_5.bungee.api.plugin.Plugin ;
2022-07-17 03:41:18 +00:00
import redis.clients.jedis.* ;
2022-04-13 18:17:38 +00:00
import redis.clients.jedis.exceptions.JedisConnectionException ;
2022-07-17 05:51:24 +00:00
import redis.clients.jedis.exceptions.JedisException ;
2022-04-13 18:17:38 +00:00
import java.io.* ;
import java.lang.reflect.Field ;
import java.net.InetAddress ;
import java.util.* ;
import java.util.concurrent.* ;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.logging.Level ;
import static com.google.common.base.Preconditions.checkArgument ;
public class RedisBungeeBungeePlugin extends Plugin implements RedisBungeePlugin < ProxiedPlayer > {
2022-07-17 03:41:18 +00:00
2022-04-13 18:17:38 +00:00
private RedisBungeeAPI api ;
2022-07-17 03:41:18 +00:00
private RedisBungeeMode redisBungeeMode ;
2022-04-13 18:17:38 +00:00
private PubSubListener psl = null ;
2022-07-25 12:49:57 +00:00
private Summoner < ? > summoner ;
2022-04-13 18:17:38 +00:00
private UUIDTranslator uuidTranslator ;
private RedisBungeeConfiguration configuration ;
private BungeeDataManager dataManager ;
private OkHttpClient httpClient ;
2022-07-17 11:38:00 +00:00
private volatile List < String > proxiesIds ;
2022-04-13 18:17:38 +00:00
private final AtomicInteger nagAboutServers = new AtomicInteger ( ) ;
private final AtomicInteger globalPlayerCount = new AtomicInteger ( ) ;
private Future < ? > integrityCheck ;
private Future < ? > heartbeatTask ;
2022-07-17 03:41:18 +00:00
private LuaManager . Script getRedisClusterTimeScript ;
2022-04-13 18:17:38 +00:00
private static final Object SERVER_TO_PLAYERS_KEY = new Object ( ) ;
private final Cache < Object , Multimap < String , UUID > > serverToPlayersCache = CacheBuilder . newBuilder ( )
. expireAfterWrite ( 5 , TimeUnit . SECONDS )
. build ( ) ;
@Override
public RedisBungeeConfiguration getConfiguration ( ) {
return this . configuration ;
}
@Override
public int getCount ( ) {
return this . globalPlayerCount . get ( ) ;
}
@Override
public int getCurrentCount ( ) {
2022-07-17 03:41:18 +00:00
return new RedisTask < Long > ( api ) {
@Override
public Long jedisTask ( Jedis jedis ) {
long total = 0 ;
long redisTime = getRedisTime ( jedis . time ( ) ) ;
Map < String , String > heartBeats = jedis . hgetAll ( " heartbeats " ) ;
for ( Map . Entry < String , String > stringStringEntry : heartBeats . entrySet ( ) ) {
String k = stringStringEntry . getKey ( ) ;
String v = stringStringEntry . getValue ( ) ;
long heartbeatTime = Long . parseLong ( v ) ;
if ( heartbeatTime + 30 > = redisTime ) {
total = total + jedis . scard ( " proxy: " + k + " :usersOnline " ) ;
}
}
return total ;
}
@Override
public Long clusterJedisTask ( JedisCluster jedisCluster ) {
long total = 0 ;
long redisTime = getRedisClusterTime ( ) ;
Map < String , String > heartBeats = jedisCluster . hgetAll ( " heartbeats " ) ;
for ( Map . Entry < String , String > stringStringEntry : heartBeats . entrySet ( ) ) {
String k = stringStringEntry . getKey ( ) ;
String v = stringStringEntry . getValue ( ) ;
long heartbeatTime = Long . parseLong ( v ) ;
if ( heartbeatTime + 30 > = redisTime ) {
total = total + jedisCluster . scard ( " proxy: " + k + " :usersOnline " ) ;
}
}
return total ;
}
} . execute ( ) . intValue ( ) ;
2022-04-13 18:17:38 +00:00
}
@Override
public Set < String > getLocalPlayersAsUuidStrings ( ) {
ImmutableSet . Builder < String > builder = ImmutableSet . builder ( ) ;
for ( ProxiedPlayer player : getProxy ( ) . getPlayers ( ) ) {
builder . add ( player . getUniqueId ( ) . toString ( ) ) ;
}
return builder . build ( ) ;
}
@Override
2022-07-07 22:39:05 +00:00
public AbstractDataManager < ProxiedPlayer , ? , ? , ? > getDataManager ( ) {
2022-04-13 18:17:38 +00:00
return this . dataManager ;
}
@Override
public Set < UUID > getPlayers ( ) {
2022-07-17 03:41:18 +00:00
return new RedisTask < Set < UUID > > ( api ) {
@Override
public Set < UUID > jedisTask ( Jedis jedis ) {
ImmutableSet . Builder < UUID > setBuilder = ImmutableSet . builder ( ) ;
try {
List < String > keys = new ArrayList < > ( ) ;
2022-07-17 11:38:00 +00:00
for ( String i : getProxiesIds ( ) ) {
2022-07-17 03:41:18 +00:00
keys . add ( " proxy: " + i + " :usersOnline " ) ;
}
if ( ! keys . isEmpty ( ) ) {
Set < String > users = jedis . sunion ( keys . toArray ( new String [ 0 ] ) ) ;
if ( users ! = null & & ! users . isEmpty ( ) ) {
for ( String user : users ) {
try {
setBuilder = setBuilder . add ( UUID . fromString ( user ) ) ;
} catch ( IllegalArgumentException ignored ) {
}
2022-04-13 18:17:38 +00:00
}
}
}
2022-07-17 03:41:18 +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 ) ;
throw new RuntimeException ( " Unable to get all players online " , e ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
return setBuilder . build ( ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
@Override
public Set < UUID > clusterJedisTask ( JedisCluster jedisCluster ) {
ImmutableSet . Builder < UUID > setBuilder = ImmutableSet . builder ( ) ;
try {
List < String > keys = new ArrayList < > ( ) ;
2022-07-17 11:38:00 +00:00
for ( String i : getProxiesIds ( ) ) {
2022-07-17 03:41:18 +00:00
keys . add ( " proxy: " + i + " :usersOnline " ) ;
}
if ( ! keys . isEmpty ( ) ) {
Set < String > users = jedisCluster . sunion ( keys . toArray ( new String [ 0 ] ) ) ;
if ( users ! = null & & ! users . isEmpty ( ) ) {
for ( String user : users ) {
try {
setBuilder = setBuilder . add ( UUID . fromString ( user ) ) ;
} catch ( IllegalArgumentException ignored ) {
}
}
}
}
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
throw new RuntimeException ( " Unable to get all players online " , e ) ;
}
return setBuilder . build ( ) ;
}
} . execute ( ) ;
2022-04-14 10:34:56 +00:00
}
2022-04-13 18:17:38 +00:00
@Override
public RedisBungeeAPI getApi ( ) {
return this . api ;
}
@Override
public UUIDTranslator getUuidTranslator ( ) {
return this . uuidTranslator ;
}
@Override
public Multimap < String , UUID > serversToPlayers ( ) {
try {
2022-07-17 03:41:18 +00:00
return serverToPlayersCache . get ( SERVER_TO_PLAYERS_KEY , new RedisTask < Multimap < String , UUID > > ( api ) {
@Override
public Multimap < String , UUID > jedisTask ( Jedis jedis ) {
ImmutableMultimap . Builder < String , UUID > builder = ImmutableMultimap . builder ( ) ;
2022-07-19 03:19:32 +00:00
for ( String serverId : getProxiesIds ( ) ) {
Set < String > players = jedis . smembers ( " proxy: " + serverId + " :usersOnline " ) ;
2022-07-17 03:41:18 +00:00
for ( String player : players ) {
2022-07-19 03:19:32 +00:00
String playerServer = jedis . hget ( " player: " + player , " server " ) ;
if ( playerServer = = null ) {
continue ;
}
builder . put ( playerServer , UUID . fromString ( player ) ) ;
2022-07-17 03:41:18 +00:00
}
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
return builder . build ( ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-08 02:47:50 +00:00
2022-07-17 03:41:18 +00:00
@Override
public Multimap < String , UUID > clusterJedisTask ( JedisCluster jedisCluster ) {
ImmutableMultimap . Builder < String , UUID > builder = ImmutableMultimap . builder ( ) ;
2022-07-19 03:19:32 +00:00
for ( String serverId : getProxiesIds ( ) ) {
Set < String > players = jedisCluster . smembers ( " proxy: " + serverId + " :usersOnline " ) ;
2022-07-17 03:41:18 +00:00
for ( String player : players ) {
2022-07-19 03:19:32 +00:00
String playerServer = jedisCluster . hget ( " player: " + player , " server " ) ;
if ( playerServer = = null ) {
continue ;
}
builder . put ( playerServer , UUID . fromString ( player ) ) ;
2022-07-17 03:41:18 +00:00
}
}
return builder . build ( ) ;
}
2022-04-13 18:17:38 +00:00
} ) ;
} catch ( ExecutionException e ) {
throw new RuntimeException ( e ) ;
}
}
@Override
public Set < UUID > getPlayersOnProxy ( String proxyId ) {
2022-07-17 11:38:00 +00:00
checkArgument ( getProxiesIds ( ) . contains ( proxyId ) , proxyId + " is not a valid proxy ID " ) ;
2022-07-17 03:41:18 +00:00
return new RedisTask < Set < UUID > > ( api ) {
@Override
public Set < UUID > jedisTask ( Jedis jedis ) {
Set < String > users = jedis . smembers ( " proxy: " + proxyId + " :usersOnline " ) ;
ImmutableSet . Builder < UUID > builder = ImmutableSet . builder ( ) ;
for ( String user : users ) {
builder . add ( UUID . fromString ( user ) ) ;
}
return builder . build ( ) ;
2022-04-13 21:16:31 +00:00
}
2022-07-17 03:41:18 +00:00
@Override
public Set < UUID > clusterJedisTask ( JedisCluster jedisCluster ) {
Set < String > users = jedisCluster . smembers ( " proxy: " + proxyId + " :usersOnline " ) ;
ImmutableSet . Builder < UUID > builder = ImmutableSet . builder ( ) ;
for ( String user : users ) {
builder . add ( UUID . fromString ( user ) ) ;
}
return builder . build ( ) ;
}
} . execute ( ) ;
2022-04-13 18:17:38 +00:00
}
@Override
2022-07-17 11:38:00 +00:00
public void sendProxyCommand ( String proxyId , String command ) {
checkArgument ( getProxiesIds ( ) . contains ( proxyId ) | | proxyId . equals ( " allservers " ) , " proxyId is invalid " ) ;
sendChannelMessage ( " redisbungee- " + proxyId , command ) ;
2022-04-13 18:17:38 +00:00
}
@Override
2022-07-17 11:38:00 +00:00
public List < String > getProxiesIds ( ) {
return proxiesIds ;
2022-04-13 18:17:38 +00:00
}
@Override
2022-07-17 11:38:00 +00:00
public List < String > getCurrentProxiesIds ( boolean nag , boolean lagged ) {
2022-07-17 03:41:18 +00:00
return new RedisTask < List < String > > ( api ) {
@Override
public List < String > jedisTask ( Jedis jedis ) {
try {
long time = getRedisTime ( jedis . time ( ) ) ;
int nagTime = 0 ;
if ( nag ) {
nagTime = nagAboutServers . decrementAndGet ( ) ;
if ( nagTime < = 0 ) {
nagAboutServers . set ( 10 ) ;
}
}
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 . parseLong ( entry . getValue ( ) ) ;
if ( lagged ? time > = stamp + 30 : time < = stamp + 30 )
servers . add ( entry . getKey ( ) ) ;
else if ( nag & & nagTime < = 0 ) {
getLogger ( ) . warning ( entry . getKey ( ) + " is " + ( time - stamp ) + " seconds behind! (Time not synchronized or server down?) and was removed from heartbeat. " ) ;
jedis . hdel ( " heartbeats " , entry . getKey ( ) ) ;
}
} catch ( NumberFormatException ignored ) {
}
}
return servers . build ( ) ;
} catch ( JedisConnectionException e ) {
getLogger ( ) . log ( Level . SEVERE , " Unable to fetch server IDs " , e ) ;
2022-07-17 11:38:00 +00:00
return Collections . singletonList ( configuration . getProxyId ( ) ) ;
2022-04-13 21:16:31 +00:00
}
}
2022-07-17 03:41:18 +00:00
@Override
public List < String > clusterJedisTask ( JedisCluster jedisCluster ) {
2022-04-13 21:16:31 +00:00
try {
2022-07-17 03:41:18 +00:00
long time = getRedisClusterTime ( ) ;
int nagTime = 0 ;
if ( nag ) {
nagTime = nagAboutServers . decrementAndGet ( ) ;
if ( nagTime < = 0 ) {
nagAboutServers . set ( 10 ) ;
}
2022-04-13 21:16:31 +00:00
}
2022-07-17 03:41:18 +00:00
ImmutableList . Builder < String > servers = ImmutableList . builder ( ) ;
Map < String , String > heartbeats = jedisCluster . hgetAll ( " heartbeats " ) ;
for ( Map . Entry < String , String > entry : heartbeats . entrySet ( ) ) {
try {
long stamp = Long . parseLong ( entry . getValue ( ) ) ;
if ( lagged ? time > = stamp + 30 : time < = stamp + 30 )
servers . add ( entry . getKey ( ) ) ;
else if ( nag & & nagTime < = 0 ) {
getLogger ( ) . warning ( entry . getKey ( ) + " is " + ( time - stamp ) + " seconds behind! (Time not synchronized or server down?) and was removed from heartbeat. " ) ;
jedisCluster . hdel ( " heartbeats " , entry . getKey ( ) ) ;
}
} catch ( NumberFormatException ignored ) {
}
}
return servers . build ( ) ;
} catch ( JedisConnectionException e ) {
getLogger ( ) . log ( Level . SEVERE , " Unable to fetch server IDs " , e ) ;
2022-07-17 11:38:00 +00:00
return Collections . singletonList ( configuration . getProxyId ( ) ) ;
2022-04-13 21:16:31 +00:00
}
}
2022-07-17 03:41:18 +00:00
} . execute ( ) ;
2022-04-13 18:17:38 +00:00
}
@Override
public PubSubListener getPubSubListener ( ) {
2022-04-13 21:16:31 +00:00
return this . psl ;
2022-04-13 18:17:38 +00:00
}
@Override
public void sendChannelMessage ( String channel , String message ) {
2022-07-17 03:41:18 +00:00
new RedisTask < Void > ( api ) {
@Override
public Void jedisTask ( Jedis jedis ) {
try {
jedis . publish ( channel , message ) ;
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
throw new RuntimeException ( " Unable to publish channel message " , e ) ;
}
return null ;
}
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
try {
jedisCluster . publish ( channel , message ) ;
} catch ( JedisConnectionException e ) {
// Redis server has disappeared!
getLogger ( ) . log ( Level . SEVERE , " Unable to get connection from pool - did your Redis server go away? " , e ) ;
throw new RuntimeException ( " Unable to publish channel message " , e ) ;
}
return null ;
}
} . execute ( ) ;
2022-04-13 18:17:38 +00:00
}
@Override
public void executeAsync ( Runnable runnable ) {
this . getProxy ( ) . getScheduler ( ) . runAsync ( this , runnable ) ;
}
@Override
public void executeAsyncAfter ( Runnable runnable , TimeUnit timeUnit , int time ) {
this . getProxy ( ) . getScheduler ( ) . schedule ( this , runnable , time , timeUnit ) ;
}
@Override
public void callEvent ( Object event ) {
this . getProxy ( ) . getPluginManager ( ) . callEvent ( ( Event ) event ) ;
}
@Override
public boolean isOnlineMode ( ) {
return this . getProxy ( ) . getConfig ( ) . isOnlineMode ( ) ;
}
@Override
public void logInfo ( String msg ) {
this . getLogger ( ) . info ( msg ) ;
}
@Override
public void logWarn ( String msg ) {
this . getLogger ( ) . warning ( msg ) ;
}
@Override
public void logFatal ( String msg ) {
this . getLogger ( ) . severe ( msg ) ;
}
@Override
public ProxiedPlayer getPlayer ( UUID uuid ) {
return this . getProxy ( ) . getPlayer ( uuid ) ;
}
@Override
public ProxiedPlayer getPlayer ( String name ) {
return this . getProxy ( ) . getPlayer ( name ) ;
}
@Override
public UUID getPlayerUUID ( String player ) {
return this . getProxy ( ) . getPlayer ( player ) . getUniqueId ( ) ;
}
@Override
public String getPlayerName ( UUID player ) {
return this . getProxy ( ) . getPlayer ( player ) . getName ( ) ;
}
@Override
public String getPlayerServerName ( ProxiedPlayer player ) {
return player . getServer ( ) . getInfo ( ) . getName ( ) ;
}
@Override
public boolean isPlayerOnAServer ( ProxiedPlayer player ) {
return player . getServer ( ) ! = null ;
}
@Override
public InetAddress getPlayerIp ( ProxiedPlayer player ) {
return player . getAddress ( ) . getAddress ( ) ;
}
@Override
public void sendProxyCommand ( String cmd ) {
2022-07-17 11:38:00 +00:00
checkArgument ( getProxiesIds ( ) . contains ( this . configuration . getProxyId ( ) ) | | this . configuration . getProxyId ( ) . equals ( " allservers " ) , " proxyId is invalid " ) ;
sendChannelMessage ( " redisbungee- " + this . configuration . getProxyId ( ) , cmd ) ;
2022-04-13 18:17:38 +00:00
}
@Override
public long getRedisTime ( List < String > timeRes ) {
return Long . parseLong ( timeRes . get ( 0 ) ) ;
}
@Override
2022-07-07 23:23:44 +00:00
public void initialize ( ) {
2022-04-13 18:17:38 +00:00
ThreadFactory factory = ( ( ThreadPoolExecutor ) getExecutorService ( ) ) . getThreadFactory ( ) ;
ScheduledExecutorService service = Executors . newScheduledThreadPool ( 24 , factory ) ;
try {
Field field = Plugin . class . getDeclaredField ( " service " ) ;
field . setAccessible ( true ) ;
ExecutorService builtinService = ( ExecutorService ) field . get ( this ) ;
field . set ( this , service ) ;
builtinService . shutdownNow ( ) ;
} catch ( IllegalAccessException | NoSuchFieldException e ) {
getLogger ( ) . log ( Level . WARNING , " Can't replace BungeeCord thread pool with our own " ) ;
getLogger ( ) . log ( Level . INFO , " skipping replacement..... " ) ;
}
try {
2022-07-25 12:49:57 +00:00
loadConfig ( this , getDataFolder ( ) ) ;
2022-04-13 18:17:38 +00:00
} catch ( IOException e ) {
throw new RuntimeException ( " Unable to load/save config " , e ) ;
}
2022-07-17 03:41:18 +00:00
// init the api class
2022-04-14 10:12:47 +00:00
this . api = new RedisBungeeAPI ( this ) ;
2022-07-17 03:41:18 +00:00
// init the http lib
httpClient = new OkHttpClient ( ) ;
Dispatcher dispatcher = new Dispatcher ( getExecutorService ( ) ) ;
httpClient . setDispatcher ( dispatcher ) ;
NameFetcher . setHttpClient ( httpClient ) ;
UUIDFetcher . setHttpClient ( httpClient ) ;
// init lua manager
LuaManager luaManager = new LuaManager ( this ) ;
2022-07-17 05:51:24 +00:00
new RedisTask < Void > ( this ) {
@Override
public Void jedisTask ( Jedis jedis ) {
// This is more portable than INFO <section>
String info = jedis . info ( ) ;
for ( String s : info . split ( " \ r \ n " ) ) {
if ( s . startsWith ( " redis_version: " ) ) {
String version = s . split ( " : " ) [ 1 ] ;
getLogger ( ) . info ( version + " <- redis version " ) ;
if ( ! RedisUtil . isRedisVersionRight ( version ) ) {
getLogger ( ) . severe ( " Your version of Redis ( " + version + " ) is not at least version 3.0 RedisBungee requires a newer version of Redis. " ) ;
throw new RuntimeException ( " Unsupported Redis version detected " ) ;
}
long uuidCacheSize = jedis . hlen ( " uuid-cache " ) ;
if ( uuidCacheSize > 750000 ) {
getLogger ( ) . info ( " Looks like you have a really big UUID cache! Run https://www.spigotmc.org/resources/redisbungeecleaner.8505/ as soon as possible. " ) ;
}
break ;
}
}
return null ;
}
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
// This is more portable than INFO <section>
try {
getRedisClusterTimeScript = luaManager . createScript ( IOUtil . readInputStreamAsString ( getResourceAsStream ( " lua/get_cluster_time.lua " ) ) ) ;
} catch ( JedisException e ) {
throw new RuntimeException ( " possible not supported redis version " , e ) ;
}
String info = ( String ) luaManager . createScript ( IOUtil . readInputStreamAsString ( getResourceAsStream ( " lua/get_cluster_info.lua " ) ) ) . eval ( Collections . singletonList ( " 0 " ) , Collections . emptyList ( ) ) ;
for ( String s : info . split ( " \ r \ n " ) ) {
if ( s . startsWith ( " redis_version: " ) ) {
String version = s . split ( " : " ) [ 1 ] ;
getLogger ( ) . info ( version + " <- redis version " ) ;
if ( ! RedisUtil . isRedisVersionRight ( version ) ) {
getLogger ( ) . severe ( " Your version of Redis ( " + version + " ) is not at least version 3.0 RedisBungee requires a newer version of Redis. " ) ;
throw new RuntimeException ( " Unsupported Redis version detected " ) ;
}
long uuidCacheSize = jedisCluster . hlen ( " uuid-cache " ) ;
if ( uuidCacheSize > 750000 ) {
getLogger ( ) . info ( " Looks like you have a really big UUID cache! Run https://www.spigotmc.org/resources/redisbungeecleaner.8505/ as soon as possible. " ) ;
}
break ;
}
}
return null ;
}
} . execute ( ) ;
2022-07-17 03:41:18 +00:00
getLogger ( ) . info ( " lua manager was loaded " ) ;
2022-07-17 05:51:24 +00:00
// check if this proxy is recovering from a crash and start heart the beat.
2022-07-17 03:41:18 +00:00
new RedisTask < Void > ( api ) {
@Override
public Void jedisTask ( Jedis jedis ) {
File crashFile = new File ( getDataFolder ( ) , " restarted_from_crash.txt " ) ;
if ( crashFile . exists ( ) & & crashFile . delete ( ) ) {
getLogger ( ) . info ( " crash file was deleted " ) ;
2022-07-17 11:38:00 +00:00
} else if ( jedis . hexists ( " heartbeats " , configuration . getProxyId ( ) ) ) {
2022-07-17 03:41:18 +00:00
try {
2022-07-17 11:38:00 +00:00
long value = Long . parseLong ( jedis . hget ( " heartbeats " , configuration . getProxyId ( ) ) ) ;
2022-07-17 03:41:18 +00:00
long redisTime = getRedisTime ( jedis . time ( ) ) ;
if ( redisTime < value + 20 ) {
getLogger ( ) . severe ( " You have launched a possible impostor Velocity / 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 impostor instance! " ) ;
}
} catch ( NumberFormatException ignored ) {
}
}
return null ;
}
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
File crashFile = new File ( getDataFolder ( ) , " restarted_from_crash.txt " ) ;
if ( crashFile . exists ( ) & & crashFile . delete ( ) ) {
getLogger ( ) . info ( " crash file was deleted " ) ;
2022-07-17 11:38:00 +00:00
} else if ( jedisCluster . hexists ( " heartbeats " , configuration . getProxyId ( ) ) ) {
2022-07-17 03:41:18 +00:00
try {
2022-07-17 11:38:00 +00:00
long value = Long . parseLong ( jedisCluster . hget ( " heartbeats " , configuration . getProxyId ( ) ) ) ;
2022-07-17 03:41:18 +00:00
long redisTime = getRedisClusterTime ( ) ;
2022-07-17 05:51:24 +00:00
2022-07-17 03:41:18 +00:00
if ( redisTime < value + 20 ) {
getLogger ( ) . severe ( " You have launched a possible impostor Velocity / 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 impostor instance! " ) ;
}
} catch ( NumberFormatException ignored ) {
}
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
return null ;
}
} . execute ( ) ;
2022-07-20 09:21:24 +00:00
updateProxyIds ( ) ;
2022-07-17 03:41:18 +00:00
uuidTranslator = new UUIDTranslator ( this ) ;
2022-07-20 09:21:24 +00:00
heartbeatTask = service . scheduleAtFixedRate ( new HeartbeatTask ( this , this . globalPlayerCount ) , 0 , HeartbeatTask . INTERVAL , HeartbeatTask . REPEAT_INTERVAL_TIME_UNIT ) ;
2022-07-17 03:41:18 +00:00
dataManager = new BungeeDataManager ( this ) ;
getProxy ( ) . getPluginManager ( ) . registerListener ( this , new RedisBungeeBungeeListener ( this , configuration . getExemptAddresses ( ) ) ) ;
getProxy ( ) . getPluginManager ( ) . registerListener ( this , dataManager ) ;
psl = new PubSubListener ( this ) ;
getProxy ( ) . getScheduler ( ) . runAsync ( this , psl ) ;
RedisTask < Void > integrityCheckRedisTask = new RedisTask < Void > ( api ) {
@Override
public Void jedisTask ( Jedis jedis ) {
try {
2022-07-08 02:47:50 +00:00
Set < String > players = getLocalPlayersAsUuidStrings ( ) ;
2022-07-17 11:38:00 +00:00
Set < String > playersInRedis = jedis . smembers ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) ;
List < String > lagged = getCurrentProxiesIds ( false , true ) ;
2022-07-08 02:47:50 +00:00
// Clean up lagged players.
for ( String s : lagged ) {
2022-07-17 03:41:18 +00:00
Set < String > laggedPlayers = jedis . smembers ( " proxy: " + s + " :usersOnline " ) ;
jedis . del ( " proxy: " + s + " :usersOnline " ) ;
2022-07-08 02:47:50 +00:00
if ( ! laggedPlayers . isEmpty ( ) ) {
getLogger ( ) . info ( " Cleaning up lagged proxy " + s + " ( " + laggedPlayers . size ( ) + " players)... " ) ;
for ( String laggedPlayer : laggedPlayers ) {
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( laggedPlayer , jedis , true ) ;
2022-04-13 18:17:38 +00:00
}
}
2022-07-08 02:47:50 +00:00
}
2022-04-13 18:17:38 +00:00
2022-07-08 02:47:50 +00:00
Set < String > absentLocally = new HashSet < > ( playersInRedis ) ;
absentLocally . removeAll ( players ) ;
Set < String > absentInRedis = new HashSet < > ( players ) ;
absentInRedis . removeAll ( playersInRedis ) ;
for ( String member : absentLocally ) {
boolean found = false ;
2022-07-17 11:38:00 +00:00
for ( String proxyId : getProxiesIds ( ) ) {
if ( proxyId . equals ( configuration . getProxyId ( ) ) ) continue ;
2022-07-17 03:41:18 +00:00
if ( jedis . sismember ( " proxy: " + proxyId + " :usersOnline " , member ) ) {
2022-07-08 02:47:50 +00:00
// Just clean up the set.
found = true ;
break ;
2022-04-13 18:17:38 +00:00
}
}
2022-07-08 02:47:50 +00:00
if ( ! found ) {
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( member , jedis , false ) ;
2022-07-08 02:47:50 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally and globally: " + member ) ;
} else {
2022-07-17 11:38:00 +00:00
jedis . srem ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " , member ) ;
2022-07-08 02:47:50 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally, but is on another proxy: " + member ) ;
}
}
2022-04-13 18:17:38 +00:00
2022-07-17 03:41:18 +00:00
Pipeline pipeline = jedis . pipelined ( ) ;
2022-04-13 18:17:38 +00:00
2022-07-08 02:47:50 +00:00
for ( String player : absentInRedis ) {
// Player not online according to Redis but not BungeeCord.
getLogger ( ) . warning ( " Player " + player + " is on the proxy but not in Redis. " ) ;
2022-04-13 18:17:38 +00:00
2022-07-08 02:47:50 +00:00
ProxiedPlayer proxiedPlayer = ProxyServer . getInstance ( ) . getPlayer ( UUID . fromString ( player ) ) ;
if ( proxiedPlayer = = null )
continue ; // We'll deal with it later.
2022-04-13 18:17:38 +00:00
2022-07-21 05:36:10 +00:00
BungeePlayerUtils . createPlayer ( proxiedPlayer , pipeline , false ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-08 02:47:50 +00:00
pipeline . sync ( ) ;
} catch ( Throwable e ) {
getLogger ( ) . log ( Level . SEVERE , " Unable to fix up stored player data " , e ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
return null ;
}
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
try {
Set < String > players = getLocalPlayersAsUuidStrings ( ) ;
2022-07-17 11:38:00 +00:00
Set < String > playersInRedis = jedisCluster . smembers ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) ;
List < String > lagged = getCurrentProxiesIds ( false , true ) ;
2022-07-17 03:41:18 +00:00
// Clean up lagged players.
for ( String s : lagged ) {
Set < String > laggedPlayers = jedisCluster . smembers ( " proxy: " + s + " :usersOnline " ) ;
jedisCluster . del ( " proxy: " + s + " :usersOnline " ) ;
if ( ! laggedPlayers . isEmpty ( ) ) {
getLogger ( ) . info ( " Cleaning up lagged proxy " + s + " ( " + laggedPlayers . size ( ) + " players)... " ) ;
for ( String laggedPlayer : laggedPlayers ) {
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( laggedPlayer , jedisCluster , true ) ;
2022-07-17 03:41:18 +00:00
}
}
}
Set < String > absentLocally = new HashSet < > ( playersInRedis ) ;
absentLocally . removeAll ( players ) ;
Set < String > absentInRedis = new HashSet < > ( players ) ;
absentInRedis . removeAll ( playersInRedis ) ;
for ( String member : absentLocally ) {
boolean found = false ;
2022-07-17 11:38:00 +00:00
for ( String proxyId : getProxiesIds ( ) ) {
if ( proxyId . equals ( configuration . getProxyId ( ) ) ) continue ;
2022-07-17 03:41:18 +00:00
if ( jedisCluster . sismember ( " proxy: " + proxyId + " :usersOnline " , member ) ) {
// Just clean up the set.
found = true ;
break ;
}
}
if ( ! found ) {
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( member , jedisCluster , false ) ;
2022-07-17 03:41:18 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally and globally: " + member ) ;
} else {
2022-07-17 11:38:00 +00:00
jedisCluster . srem ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " , member ) ;
2022-07-17 03:41:18 +00:00
getLogger ( ) . warning ( " Player found in set that was not found locally, but is on another proxy: " + member ) ;
}
}
// due JedisCluster does not support pipelined.
//Pipeline pipeline = jedis.pipelined();
for ( String player : absentInRedis ) {
// Player not online according to Redis but not BungeeCord.
getLogger ( ) . warning ( " Player " + player + " is on the proxy but not in Redis. " ) ;
ProxiedPlayer proxiedPlayer = ProxyServer . getInstance ( ) . getPlayer ( UUID . fromString ( player ) ) ;
if ( proxiedPlayer = = null )
continue ; // We'll deal with it later.
2022-07-20 08:32:04 +00:00
BungeePlayerUtils . createPlayer ( proxiedPlayer , jedisCluster , true ) ;
2022-07-17 03:41:18 +00:00
}
} catch ( Throwable e ) {
getLogger ( ) . log ( Level . SEVERE , " Unable to fix up stored player data " , e ) ;
}
return null ;
}
} ;
2022-07-19 03:21:17 +00:00
integrityCheck = service . scheduleAtFixedRate ( integrityCheckRedisTask : : execute , 0 , 30 , TimeUnit . SECONDS ) ;
2022-07-17 03:41:18 +00:00
// register plugin messages channel.
2022-04-13 18:17:38 +00:00
getProxy ( ) . registerChannel ( " legacy:redisbungee " ) ;
getProxy ( ) . registerChannel ( " RedisBungee " ) ;
2022-07-22 12:10:06 +00:00
if ( configuration . doRegisterLegacyCommands ( ) ) {
// register commands
if ( configuration . doOverrideBungeeCommands ( ) ) {
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 ( this ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . PlayerProxyCommand ( this ) ) ;
getProxy ( ) . getPluginManager ( ) . registerCommand ( this , new RedisBungeeCommands . PlistCommand ( this ) ) ;
2022-06-15 02:32:22 +00:00
}
2022-04-13 18:17:38 +00:00
}
@Override
2022-07-06 19:04:09 +00:00
public void stop ( ) {
2022-07-17 03:41:18 +00:00
// Poison the PubSub listener
if ( psl ! = null ) {
2022-04-13 18:17:38 +00:00
psl . poison ( ) ;
2022-07-17 03:41:18 +00:00
}
if ( integrityCheck ! = null ) {
2022-04-13 18:17:38 +00:00
integrityCheck . cancel ( true ) ;
2022-07-17 03:41:18 +00:00
}
if ( heartbeatTask ! = null ) {
2022-04-13 18:17:38 +00:00
heartbeatTask . cancel ( true ) ;
2022-07-17 03:41:18 +00:00
}
getProxy ( ) . getPluginManager ( ) . unregisterListeners ( this ) ;
2022-04-13 18:17:38 +00:00
2022-07-17 03:41:18 +00:00
new RedisTask < Void > ( api ) {
@Override
public Void jedisTask ( Jedis jedis ) {
2022-07-17 11:38:00 +00:00
jedis . hdel ( " heartbeats " , configuration . getProxyId ( ) ) ;
if ( jedis . scard ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) > 0 ) {
Set < String > players = jedis . smembers ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) ;
2022-04-13 18:17:38 +00:00
for ( String member : players )
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( member , jedis , true ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
return null ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
2022-07-17 11:38:00 +00:00
jedisCluster . hdel ( " heartbeats " , configuration . getProxyId ( ) ) ;
if ( jedisCluster . scard ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) > 0 ) {
Set < String > players = jedisCluster . smembers ( " proxy: " + configuration . getProxyId ( ) + " :usersOnline " ) ;
2022-07-17 03:41:18 +00:00
for ( String member : players )
2022-07-21 05:36:10 +00:00
GenericPlayerUtils . cleanUpPlayer ( member , jedisCluster , true ) ;
2022-07-17 03:41:18 +00:00
}
return null ;
2022-07-06 19:04:09 +00:00
}
2022-07-17 03:41:18 +00:00
} . execute ( ) ;
try {
2022-07-25 12:49:57 +00:00
this . summoner . close ( ) ;
2022-07-17 03:41:18 +00:00
} catch ( IOException e ) {
throw new RuntimeException ( e ) ;
2022-04-13 18:17:38 +00:00
}
2022-07-17 03:41:18 +00:00
2022-04-13 18:17:38 +00:00
}
2022-07-15 22:58:48 +00:00
@Override
2022-07-17 03:41:18 +00:00
public Summoner < ? > getSummoner ( ) {
2022-07-25 12:49:57 +00:00
return this . summoner ;
2022-07-15 22:58:48 +00:00
}
2022-04-13 18:17:38 +00:00
2022-07-19 11:30:45 +00:00
@Override
public void kickPlayer ( UUID playerUniqueId , String message ) {
2022-07-19 11:43:58 +00:00
// first handle on origin proxy if player not found publish the payload
if ( ! dataManager . handleKick ( playerUniqueId , message ) ) {
new RedisTask < Void > ( api ) {
@Override
public Void jedisTask ( Jedis jedis ) {
2022-07-20 08:32:04 +00:00
PayloadUtils . kickPlayerPayload ( playerUniqueId , message , jedis ) ;
2022-07-19 11:43:58 +00:00
return null ;
}
2022-07-19 11:30:45 +00:00
2022-07-19 11:43:58 +00:00
@Override
public Void clusterJedisTask ( JedisCluster jedisCluster ) {
2022-07-20 08:32:04 +00:00
PayloadUtils . kickPlayerPayload ( playerUniqueId , message , jedisCluster ) ;
2022-07-19 11:43:58 +00:00
return null ;
}
} . execute ( ) ;
}
2022-07-19 11:30:45 +00:00
}
@Override
public void kickPlayer ( String playerName , String message ) {
// fetch the uuid
2022-07-19 11:43:58 +00:00
UUID playerUUID = this . uuidTranslator . getTranslatedUuid ( playerName , true ) ;
2022-07-19 11:30:45 +00:00
kickPlayer ( playerUUID , message ) ;
}
2022-07-17 03:41:18 +00:00
@Override
public RedisBungeeMode getRedisBungeeMode ( ) {
return this . redisBungeeMode ;
}
@Override
public Long getRedisClusterTime ( ) {
return getRedisTime ( ( List < String > ) this . getRedisClusterTimeScript . eval ( Collections . singletonList ( " 0 " ) , Collections . emptyList ( ) ) ) ;
}
2022-07-20 09:21:24 +00:00
@Override
public void updateProxyIds ( ) {
proxiesIds = getCurrentProxiesIds ( true , false ) ;
}
2022-04-13 18:17:38 +00:00
@Override
public void onEnable ( ) {
2022-07-07 23:23:44 +00:00
initialize ( ) ;
2022-04-13 18:17:38 +00:00
}
@Override
public void onDisable ( ) {
2022-07-06 19:04:09 +00:00
stop ( ) ;
2022-04-13 18:17:38 +00:00
}
2022-04-13 21:16:31 +00:00
@Override
2022-07-20 08:32:04 +00:00
public Object createPlayerChangedNetworkEvent ( UUID uuid , String previousServer , String server ) {
return new PlayerChangedServerNetworkEvent ( uuid , previousServer , server ) ;
2022-04-13 21:16:31 +00:00
}
@Override
2022-07-20 08:32:04 +00:00
public Object createPlayerJoinedNetworkEvent ( UUID uuid ) {
return new PlayerJoinedNetworkEvent ( uuid ) ;
2022-04-13 21:16:31 +00:00
}
@Override
2022-07-20 08:32:04 +00:00
public Object createPlayerLeftNetworkEvent ( UUID uuid ) {
return new PlayerLeftNetworkEvent ( uuid ) ;
2022-04-13 21:16:31 +00:00
}
@Override
2022-07-20 08:32:04 +00:00
public Object createPubSubEvent ( String channel , String message ) {
return new PubSubMessageEvent ( channel , message ) ;
2022-04-13 21:16:31 +00:00
}
2022-07-22 11:12:32 +00:00
@Override
2022-07-25 12:49:57 +00:00
public void onConfigLoad ( RedisBungeeConfiguration configuration , Summoner < ? > summoner , RedisBungeeMode mode ) {
this . configuration = configuration ;
this . redisBungeeMode = mode ;
this . summoner = summoner ;
2022-07-22 11:12:32 +00:00
}
2022-04-13 18:17:38 +00:00
}