mirror of
https://github.com/proxiodev/RedisBungee.git
synced 2024-11-23 04:28:01 +00:00
finished but untest
This commit is contained in:
parent
9f09ed21f1
commit
7de457b6fa
@ -10,6 +10,18 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>RedisBungee-API</artifactId>
|
<artifactId>RedisBungee-API</artifactId>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>8</source>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -147,9 +147,10 @@ public abstract class DataManager<P, PL, PD, PS> {
|
|||||||
serverCache.invalidate(uuid);
|
serverCache.invalidate(uuid);
|
||||||
proxyCache.invalidate(uuid);
|
proxyCache.invalidate(uuid);
|
||||||
}
|
}
|
||||||
// Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
|
|
||||||
|
|
||||||
public abstract void onPostLogin(PL event);
|
public abstract void onPostLogin(PL event);
|
||||||
// Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
|
|
||||||
public abstract void onPlayerDisconnect(PD event);
|
public abstract void onPlayerDisconnect(PD event);
|
||||||
|
|
||||||
public abstract void onPubSubMessage(PS event);
|
public abstract void onPubSubMessage(PS event);
|
||||||
@ -227,7 +228,6 @@ public abstract class DataManager<P, PL, PD, PS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class DataManagerMessage {
|
public static class DataManagerMessage {
|
||||||
private final UUID target;
|
private final UUID target;
|
||||||
private final String source;
|
private final String source;
|
||||||
|
@ -17,20 +17,30 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
public interface RedisBungeePlugin<P> {
|
public interface RedisBungeePlugin<P> {
|
||||||
|
|
||||||
void enable();
|
default void enable() {
|
||||||
|
|
||||||
void disable();
|
}
|
||||||
|
|
||||||
|
default void disable() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
RedisBungeeConfiguration getConfiguration();
|
RedisBungeeConfiguration getConfiguration();
|
||||||
|
|
||||||
int getCount();
|
int getCount();
|
||||||
|
|
||||||
|
int getCurrentCount();
|
||||||
|
|
||||||
|
Set<String> getLocalPlayersAsUuidStrings();
|
||||||
|
|
||||||
DataManager<P, ?, ?, ?> getDataManager();
|
DataManager<P, ?, ?, ?> getDataManager();
|
||||||
|
|
||||||
Set<UUID> getPlayers();
|
Set<UUID> getPlayers();
|
||||||
|
|
||||||
Jedis requestJedis();
|
Jedis requestJedis();
|
||||||
|
|
||||||
|
boolean isJedisAvailable();
|
||||||
|
|
||||||
RedisBungeeAPI getApi();
|
RedisBungeeAPI getApi();
|
||||||
|
|
||||||
UUIDTranslator getUuidTranslator();
|
UUIDTranslator getUuidTranslator();
|
||||||
@ -43,15 +53,17 @@ public interface RedisBungeePlugin<P> {
|
|||||||
|
|
||||||
List<String> getServerIds();
|
List<String> getServerIds();
|
||||||
|
|
||||||
|
List<String > getCurrentServerIds(boolean nag, boolean lagged);
|
||||||
|
|
||||||
PubSubListener getPubSubListener();
|
PubSubListener getPubSubListener();
|
||||||
|
|
||||||
void sendChannelMessage(String channel, String message);
|
void sendChannelMessage(String channel, String message);
|
||||||
|
|
||||||
void executeAsync(Runnable runnable);
|
void executeAsync(Runnable runnable);
|
||||||
|
|
||||||
void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int seconds);
|
void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
|
||||||
|
|
||||||
void callEvent(Object object);
|
void callEvent(Object event);
|
||||||
|
|
||||||
boolean isOnlineMode();
|
boolean isOnlineMode();
|
||||||
|
|
||||||
@ -61,8 +73,6 @@ public interface RedisBungeePlugin<P> {
|
|||||||
|
|
||||||
void logFatal(String msg);
|
void logFatal(String msg);
|
||||||
|
|
||||||
boolean isPlayerServerNull(P player);
|
|
||||||
|
|
||||||
P getPlayer(UUID uuid);
|
P getPlayer(UUID uuid);
|
||||||
|
|
||||||
P getPlayer(String name);
|
P getPlayer(String name);
|
||||||
@ -77,7 +87,7 @@ public interface RedisBungeePlugin<P> {
|
|||||||
|
|
||||||
InetAddress getPlayerIp(P player);
|
InetAddress getPlayerIp(P player);
|
||||||
|
|
||||||
void executeProxyCommand(String cmd);
|
void sendProxyCommand(String cmd);
|
||||||
|
|
||||||
default Class<?> getPubSubEventClass() {
|
default Class<?> getPubSubEventClass() {
|
||||||
return PubSubMessageEvent.class;
|
return PubSubMessageEvent.class;
|
||||||
@ -95,4 +105,8 @@ public interface RedisBungeePlugin<P> {
|
|||||||
return PlayerLeftNetworkEvent.class;
|
return PlayerLeftNetworkEvent.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long getRedisTime(List<String> timeRes);
|
||||||
|
|
||||||
|
void loadConfig() throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
24
RedisBungee-API/src/main/resources/lua/get_player_count.lua
Normal file
24
RedisBungee-API/src/main/resources/lua/get_player_count.lua
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
local c = redis.call
|
||||||
|
|
||||||
|
local curTime = c("TIME")
|
||||||
|
local time = tonumber(curTime[1])
|
||||||
|
|
||||||
|
local heartbeats = c("HGETALL", "heartbeats")
|
||||||
|
local total = 0
|
||||||
|
local key
|
||||||
|
|
||||||
|
for _, v in ipairs(heartbeats) do
|
||||||
|
if not key then
|
||||||
|
key = v
|
||||||
|
else
|
||||||
|
local n = tonumber(v)
|
||||||
|
if n then
|
||||||
|
if n + 30 >= time then
|
||||||
|
total = total + c("SCARD", "proxy:" .. key .. ":usersOnline")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
key = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return total
|
18
RedisBungee-API/src/main/resources/lua/server_to_players.lua
Normal file
18
RedisBungee-API/src/main/resources/lua/server_to_players.lua
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
local call = redis.call
|
||||||
|
local ipairs = ipairs
|
||||||
|
|
||||||
|
local serverToData = {}
|
||||||
|
|
||||||
|
for _, proxy in ipairs(ARGV) do
|
||||||
|
local players = call("SMEMBERS", "proxy:" .. proxy .. ":usersOnline")
|
||||||
|
for _, player in ipairs(players) do
|
||||||
|
local server = call("HGET", "player:" .. player, "server")
|
||||||
|
if server then
|
||||||
|
local sz = #serverToData
|
||||||
|
serverToData[sz + 1] = server
|
||||||
|
serverToData[sz + 2] = player
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return serverToData
|
@ -81,14 +81,6 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<source>8</source>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
@ -6,24 +6,29 @@ import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
|
|||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
|
||||||
public class BungeeDataManager extends DataManager<ProxiedPlayer, PostLoginEvent, PlayerDisconnectEvent, PubSubMessageEvent> {
|
public class BungeeDataManager extends DataManager<ProxiedPlayer, PostLoginEvent, PlayerDisconnectEvent, PubSubMessageEvent> implements Listener {
|
||||||
|
|
||||||
public BungeeDataManager(RedisBungeePlugin<ProxiedPlayer> plugin) {
|
public BungeeDataManager(RedisBungeePlugin<ProxiedPlayer> plugin) {
|
||||||
super(plugin);
|
super(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@EventHandler
|
||||||
public void onPostLogin(PostLoginEvent event) {
|
public void onPostLogin(PostLoginEvent event) {
|
||||||
invalidate(event.getPlayer().getUniqueId());
|
invalidate(event.getPlayer().getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@EventHandler
|
||||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||||
invalidate(event.getPlayer().getUniqueId());
|
invalidate(event.getPlayer().getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@EventHandler
|
||||||
public void onPubSubMessage(PubSubMessageEvent event) {
|
public void onPubSubMessage(PubSubMessageEvent event) {
|
||||||
handlePubSubMessage(event.getChannel(), event.getMessage());
|
handlePubSubMessage(event.getChannel(), event.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,571 @@
|
|||||||
|
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;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.*;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.util.IOUtil;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.util.LuaManager;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.NameFetcher;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDFetcher;
|
||||||
|
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator;
|
||||||
|
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;
|
||||||
|
import net.md_5.bungee.config.Configuration;
|
||||||
|
import net.md_5.bungee.config.ConfigurationProvider;
|
||||||
|
import net.md_5.bungee.config.YamlConfiguration;
|
||||||
|
import redis.clients.jedis.Jedis;
|
||||||
|
import redis.clients.jedis.JedisPool;
|
||||||
|
import redis.clients.jedis.JedisPoolConfig;
|
||||||
|
import redis.clients.jedis.Pipeline;
|
||||||
|
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||||
|
|
||||||
|
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> {
|
||||||
|
|
||||||
|
private static final Gson gson = new Gson();
|
||||||
|
private RedisBungeeAPI api;
|
||||||
|
private PubSubListener psl = null;
|
||||||
|
private JedisPool jedisPool;
|
||||||
|
private UUIDTranslator uuidTranslator;
|
||||||
|
private RedisBungeeConfiguration configuration;
|
||||||
|
private BungeeDataManager dataManager;
|
||||||
|
private OkHttpClient httpClient;
|
||||||
|
private volatile List<String> serverIds;
|
||||||
|
private final AtomicInteger nagAboutServers = new AtomicInteger();
|
||||||
|
private final AtomicInteger globalPlayerCount = new AtomicInteger();
|
||||||
|
private Future<?> integrityCheck;
|
||||||
|
private Future<?> heartbeatTask;
|
||||||
|
private LuaManager.Script serverToPlayersScript;
|
||||||
|
private LuaManager.Script getPlayerCountScript;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
Long count = (Long) getPlayerCountScript.eval(ImmutableList.<String>of(), ImmutableList.<String>of());
|
||||||
|
return count.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
public DataManager<ProxiedPlayer, ?, ?, ?> getDataManager() {
|
||||||
|
return this.dataManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<UUID> getPlayers() {
|
||||||
|
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
|
||||||
|
if (isJedisAvailable()) {
|
||||||
|
try (Jedis rsc = requestJedis()) {
|
||||||
|
List<String> keys = new ArrayList<>();
|
||||||
|
for (String i : getServerIds()) {
|
||||||
|
keys.add("proxy:" + i + ":usersOnline");
|
||||||
|
}
|
||||||
|
if (!keys.isEmpty()) {
|
||||||
|
Set<String> users = rsc.sunion(keys.toArray(new String[keys.size()]));
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Jedis requestJedis() {
|
||||||
|
return this.jedisPool.getResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isJedisAvailable() {
|
||||||
|
return !jedisPool.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RedisBungeeAPI getApi() {
|
||||||
|
return this.api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUIDTranslator getUuidTranslator() {
|
||||||
|
return this.uuidTranslator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Multimap<String, UUID> serversToPlayers() {
|
||||||
|
try {
|
||||||
|
return serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, new Callable<Multimap<String, UUID>>() {
|
||||||
|
@Override
|
||||||
|
public Multimap<String, UUID> call() throws Exception {
|
||||||
|
Collection<String> data = (Collection<String>) serverToPlayersScript.eval(ImmutableList.<String>of(), getServerIds());
|
||||||
|
ImmutableMultimap.Builder<String, UUID> builder = ImmutableMultimap.builder();
|
||||||
|
String key = null;
|
||||||
|
for (String s : data) {
|
||||||
|
if (key == null) {
|
||||||
|
key = s;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.put(key, UUID.fromString(s));
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<UUID> getPlayersOnProxy(String proxyId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendProxyCommand(String serverId, String command) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getServerIds() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getCurrentServerIds(boolean nag, boolean lagged) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PubSubListener getPubSubListener() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendChannelMessage(String channel, String message) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
checkArgument(getServerIds().contains(this.configuration.getServerId()) || this.configuration.getServerId().equals("allservers"), "proxyId is invalid");
|
||||||
|
sendChannelMessage("redisbungee-" + this.configuration.getServerId(), cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRedisTime(List<String> timeRes) {
|
||||||
|
return Long.parseLong(timeRes.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
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 {
|
||||||
|
loadConfig();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to load/save config", e);
|
||||||
|
} catch (JedisConnectionException e) {
|
||||||
|
throw new RuntimeException("Unable to connect to your Redis server!", e);
|
||||||
|
}
|
||||||
|
api = new RedisBungeeAPI(this);
|
||||||
|
if (isJedisAvailable()) {
|
||||||
|
try (Jedis tmpRsc = requestJedis()) {
|
||||||
|
// This is more portable than INFO <section>
|
||||||
|
String info = tmpRsc.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().warning("Your version of Redis (" + version + ") is not at least version 6.0 RedisBungee requires a newer version of Redis.");
|
||||||
|
throw new RuntimeException("Unsupported Redis version detected");
|
||||||
|
} else {
|
||||||
|
LuaManager manager = new LuaManager(this);
|
||||||
|
serverToPlayersScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/server_to_players.lua")));
|
||||||
|
getPlayerCountScript = manager.createScript(IOUtil.readInputStreamAsString(getResourceAsStream("lua/get_player_count.lua")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpRsc.hset("heartbeats", configuration.getServerId(), tmpRsc.time().get(0));
|
||||||
|
|
||||||
|
long uuidCacheSize = tmpRsc.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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serverIds = getCurrentServerIds(true, false);
|
||||||
|
uuidTranslator = new UUIDTranslator(this);
|
||||||
|
heartbeatTask = service.scheduleAtFixedRate(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try (Jedis rsc = requestJedis()) {
|
||||||
|
long redisTime = getRedisTime(rsc.time());
|
||||||
|
rsc.hset("heartbeats", configuration.getServerId(), String.valueOf(redisTime));
|
||||||
|
} catch (JedisConnectionException e) {
|
||||||
|
// Redis server has disappeared!
|
||||||
|
getLogger().log(Level.SEVERE, "Unable to update heartbeat - did your Redis server go away?", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
serverIds = getCurrentServerIds(true, false);
|
||||||
|
globalPlayerCount.set(getCurrentCount());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
getLogger().log(Level.SEVERE, "Unable to update data - did your Redis server go away?", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 3, TimeUnit.SECONDS);
|
||||||
|
dataManager = new BungeeDataManager(this);
|
||||||
|
/*if (configuration.isRegisterBungeeCommands()) {
|
||||||
|
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());
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlayerProxyCommand(this));
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlistCommand(this));
|
||||||
|
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.DebugCommand(this));
|
||||||
|
*/
|
||||||
|
|
||||||
|
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this, configuration.getExemptAddresses()));
|
||||||
|
getProxy().getPluginManager().registerListener(this, dataManager);
|
||||||
|
psl = new PubSubListener(this);
|
||||||
|
getProxy().getScheduler().runAsync(this, psl);
|
||||||
|
integrityCheck = service.scheduleAtFixedRate(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try (Jedis tmpRsc = requestJedis()) {
|
||||||
|
Set<String> players = getLocalPlayersAsUuidStrings();
|
||||||
|
Set<String> playersInRedis = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
|
||||||
|
List<String> lagged = getCurrentServerIds(false, true);
|
||||||
|
|
||||||
|
// Clean up lagged players.
|
||||||
|
for (String s : lagged) {
|
||||||
|
Set<String> laggedPlayers = tmpRsc.smembers("proxy:" + s + ":usersOnline");
|
||||||
|
tmpRsc.del("proxy:" + s + ":usersOnline");
|
||||||
|
if (!laggedPlayers.isEmpty()) {
|
||||||
|
getLogger().info("Cleaning up lagged proxy " + s + " (" + laggedPlayers.size() + " players)...");
|
||||||
|
for (String laggedPlayer : laggedPlayers) {
|
||||||
|
RedisUtil.cleanUpPlayer(laggedPlayer, tmpRsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
for (String proxyId : getServerIds()) {
|
||||||
|
if (proxyId.equals(configuration.getServerId())) continue;
|
||||||
|
if (tmpRsc.sismember("proxy:" + proxyId + ":usersOnline", member)) {
|
||||||
|
// Just clean up the set.
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
RedisUtil.cleanUpPlayer(member, tmpRsc);
|
||||||
|
getLogger().warning("Player found in set that was not found locally and globally: " + member);
|
||||||
|
} else {
|
||||||
|
tmpRsc.srem("proxy:" + configuration.getServerId() + ":usersOnline", member);
|
||||||
|
getLogger().warning("Player found in set that was not found locally, but is on another proxy: " + member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline pipeline = tmpRsc.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.
|
||||||
|
|
||||||
|
RBUtils.createPlayer(proxiedPlayer, pipeline, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.sync();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
getLogger().log(Level.SEVERE, "Unable to fix up stored player data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 1, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
getProxy().registerChannel("legacy:redisbungee");
|
||||||
|
getProxy().registerChannel("RedisBungee");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
if (isJedisAvailable()) {
|
||||||
|
// Poison the PubSub listener
|
||||||
|
psl.poison();
|
||||||
|
integrityCheck.cancel(true);
|
||||||
|
heartbeatTask.cancel(true);
|
||||||
|
getProxy().getPluginManager().unregisterListeners(this);
|
||||||
|
|
||||||
|
try (Jedis tmpRsc = requestJedis()) {
|
||||||
|
tmpRsc.hdel("heartbeats", configuration.getServerId());
|
||||||
|
if (tmpRsc.scard("proxy:" + configuration.getServerId() + ":usersOnline") > 0) {
|
||||||
|
Set<String> players = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
|
||||||
|
for (String member : players)
|
||||||
|
RedisUtil.cleanUpPlayer(member, tmpRsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jedisPool.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadConfig() throws IOException {
|
||||||
|
if (!getDataFolder().exists()) {
|
||||||
|
getDataFolder().mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(getDataFolder(), "config.yml");
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
try (InputStream in = getResourceAsStream("example_config.yml");
|
||||||
|
OutputStream out = new FileOutputStream(file)) {
|
||||||
|
ByteStreams.copy(in, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Configuration yamlConfiguration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file);
|
||||||
|
|
||||||
|
final String redisServer = yamlConfiguration.getString("redis-server", "localhost");
|
||||||
|
final int redisPort = yamlConfiguration.getInt("redis-port", 6379);
|
||||||
|
final boolean useSSL = yamlConfiguration.getBoolean("useSSL", false);
|
||||||
|
String redisPassword = yamlConfiguration.getString("redis-password", "");
|
||||||
|
String serverId = yamlConfiguration.getString("server-id");
|
||||||
|
final String randomUUID = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
// check redis password
|
||||||
|
if (redisPassword != null && (redisPassword.isEmpty() || redisPassword.equals("none"))) {
|
||||||
|
redisPassword = null;
|
||||||
|
getLogger().warning("INSECURE setup was detected Please set password for your redis instance.");
|
||||||
|
}
|
||||||
|
if (!useSSL) {
|
||||||
|
getLogger().warning("INSECURE setup was detected Please setup ssl for your redis instance.");
|
||||||
|
}
|
||||||
|
// Configuration sanity checks.
|
||||||
|
if (serverId == null || serverId.isEmpty()) {
|
||||||
|
/*
|
||||||
|
* this check causes the config comments to disappear somehow
|
||||||
|
* I think due snake yaml limitations so as todo: write our own yaml parser?
|
||||||
|
*/
|
||||||
|
String genId = UUID.randomUUID().toString();
|
||||||
|
getLogger().info("Generated server id " + genId + " and saving it to config.");
|
||||||
|
yamlConfiguration.set("server-id", genId);
|
||||||
|
ConfigurationProvider.getProvider(YamlConfiguration.class).save(yamlConfiguration, new File(getDataFolder(), "config.yml"));
|
||||||
|
getLogger().info("Server id was generated: " + serverId);
|
||||||
|
} else {
|
||||||
|
getLogger().info("Loaded server id " + serverId + '.');
|
||||||
|
}
|
||||||
|
this.configuration = new RedisBungeeConfiguration(serverId, yamlConfiguration.getStringList("exempt-ip-addresses"));
|
||||||
|
|
||||||
|
if (redisServer != null && !redisServer.isEmpty()) {
|
||||||
|
try {
|
||||||
|
JedisPoolConfig config = new JedisPoolConfig();
|
||||||
|
config.setMaxTotal(yamlConfiguration.getInt("max-redis-connections", 8));
|
||||||
|
this.jedisPool = new JedisPool(config, redisServer, redisPort, 0, redisPassword, useSSL);
|
||||||
|
|
||||||
|
} catch (JedisConnectionException e) {
|
||||||
|
throw new RuntimeException("Unable to create Redis pool", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the connection
|
||||||
|
try (Jedis rsc = requestJedis()) {
|
||||||
|
rsc.ping();
|
||||||
|
// 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.hexists("heartbeats", serverId)) {
|
||||||
|
try {
|
||||||
|
long value = Long.parseLong(rsc.hget("heartbeats", serverId));
|
||||||
|
long redisTime = getRedisTime(rsc.time());
|
||||||
|
if (redisTime < value + 20) {
|
||||||
|
getLogger().severe("You have launched a possible impostor 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
httpClient = new OkHttpClient();
|
||||||
|
Dispatcher dispatcher = new Dispatcher(getExecutorService());
|
||||||
|
httpClient.setDispatcher(dispatcher);
|
||||||
|
NameFetcher.setHttpClient(httpClient);
|
||||||
|
UUIDFetcher.setHttpClient(httpClient);
|
||||||
|
|
||||||
|
getLogger().log(Level.INFO, "Successfully connected to Redis.");
|
||||||
|
} catch (JedisConnectionException e) {
|
||||||
|
this.jedisPool.destroy();
|
||||||
|
this.jedisPool = null;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("No redis server specified!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
disable();
|
||||||
|
}
|
||||||
|
}
|
@ -254,7 +254,7 @@ public class RedisBungeeListener extends AbstractRedisBungeeListener<LoginEvent,
|
|||||||
if (message.startsWith("/"))
|
if (message.startsWith("/"))
|
||||||
message = message.substring(1);
|
message = message.substring(1);
|
||||||
plugin.logInfo("Invoking command via PubSub: /" + message);
|
plugin.logInfo("Invoking command via PubSub: /" + message);
|
||||||
plugin.executeProxyCommand(message);
|
plugin.sendProxyCommand(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
RedisBungee-Bungee/src/main/resources/plugin.yml
Normal file
9
RedisBungee-Bungee/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name: RedisBungee
|
||||||
|
main: com.imaginarycode.minecraft.redisbungee.RedisBungeeBungeePlugin
|
||||||
|
version: ${project.version}
|
||||||
|
author: Chunkr and Govindas limework
|
||||||
|
authors:
|
||||||
|
- chunkr
|
||||||
|
- Govindas Limework
|
||||||
|
# This is used so that we can automatically override default BungeeCord behavior.
|
||||||
|
softDepends: ["cmd_find", "cmd_list"]
|
Loading…
Reference in New Issue
Block a user