2022-07-06 22:24:08 +00:00
|
|
|
package com.imaginarycode.minecraft.redisbungee;
|
|
|
|
|
|
|
|
import com.google.common.cache.Cache;
|
|
|
|
import com.google.common.cache.CacheBuilder;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
|
|
import com.google.common.collect.Multimap;
|
|
|
|
import com.google.inject.Inject;
|
2022-07-17 05:51:24 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.*;
|
2022-07-22 11:12:32 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
|
2022-07-17 05:51:24 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
|
2022-07-26 06:58:00 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.api.tasks.*;
|
2022-07-17 05:51:24 +00:00
|
|
|
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-07-15 06:52:53 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands;
|
2022-07-06 22:24:08 +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-07-06 22:24:08 +00:00
|
|
|
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
|
|
|
import com.squareup.okhttp.Dispatcher;
|
|
|
|
import com.squareup.okhttp.OkHttpClient;
|
|
|
|
import com.velocitypowered.api.event.Subscribe;
|
|
|
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
|
|
|
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
2022-07-07 00:16:09 +00:00
|
|
|
import com.velocitypowered.api.plugin.Plugin;
|
2022-07-06 22:24:08 +00:00
|
|
|
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
|
|
|
import com.velocitypowered.api.proxy.Player;
|
|
|
|
import com.velocitypowered.api.proxy.ProxyServer;
|
2022-07-15 06:52:53 +00:00
|
|
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
2022-07-14 23:05:53 +00:00
|
|
|
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
|
|
|
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
2022-07-06 22:24:08 +00:00
|
|
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
|
|
|
import org.slf4j.Logger;
|
2022-07-17 05:51:24 +00:00
|
|
|
import redis.clients.jedis.*;
|
2022-07-06 22:24:08 +00:00
|
|
|
import redis.clients.jedis.exceptions.JedisConnectionException;
|
2022-07-08 02:47:50 +00:00
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
|
|
|
|
import java.io.*;
|
|
|
|
import java.net.InetAddress;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
2022-07-21 12:37:32 +00:00
|
|
|
@Plugin(id = "redisbungee", name = "RedisBungee", version = PomData.VERSION, url = "https://github.com/ProxioDev/RedisBungee", authors = {"astei", "ProxioDev"})
|
2022-07-06 22:24:08 +00:00
|
|
|
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player> {
|
|
|
|
private final ProxyServer server;
|
|
|
|
private final Logger logger;
|
2022-07-15 06:52:53 +00:00
|
|
|
private final Path dataFolder;
|
2022-07-27 13:43:51 +00:00
|
|
|
private final AbstractRedisBungeeAPI api;
|
2022-07-08 02:47:50 +00:00
|
|
|
private final PubSubListener psl;
|
2022-07-17 05:51:24 +00:00
|
|
|
private Summoner<?> jedisSummoner;
|
|
|
|
private RedisBungeeMode redisBungeeMode;
|
2022-07-08 02:47:50 +00:00
|
|
|
private final UUIDTranslator uuidTranslator;
|
2022-07-06 22:24:08 +00:00
|
|
|
private RedisBungeeConfiguration configuration;
|
2022-07-08 02:47:50 +00:00
|
|
|
private final VelocityDataManager dataManager;
|
|
|
|
private final OkHttpClient httpClient;
|
2022-07-17 11:38:00 +00:00
|
|
|
private volatile List<String> proxiesIds;
|
2022-07-06 22:24:08 +00:00
|
|
|
private final AtomicInteger globalPlayerCount = new AtomicInteger();
|
|
|
|
private ScheduledTask integrityCheck;
|
|
|
|
private ScheduledTask heartbeatTask;
|
2022-07-17 05:51:24 +00:00
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
private static final Object SERVER_TO_PLAYERS_KEY = new Object();
|
2022-07-15 06:52:53 +00:00
|
|
|
public static final List<ChannelIdentifier> IDENTIFIERS = List.of(
|
2022-07-17 05:51:24 +00:00
|
|
|
MinecraftChannelIdentifier.create("legacy", "redisbungee"),
|
2022-07-22 19:52:15 +00:00
|
|
|
new LegacyChannelIdentifier("RedisBungee"),
|
|
|
|
// This is needed for clients before 1.13
|
|
|
|
new LegacyChannelIdentifier("legacy:redisbungee")
|
2022-07-15 06:52:53 +00:00
|
|
|
);
|
2022-07-06 22:24:08 +00:00
|
|
|
private final Cache<Object, Multimap<String, UUID>> serverToPlayersCache = CacheBuilder.newBuilder()
|
|
|
|
.expireAfterWrite(5, TimeUnit.SECONDS)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
public RedisBungeeVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
|
|
|
|
this.server = server;
|
|
|
|
this.logger = logger;
|
2022-07-15 06:52:53 +00:00
|
|
|
this.dataFolder = dataDirectory;
|
2022-07-08 02:47:50 +00:00
|
|
|
try {
|
2022-07-25 12:49:57 +00:00
|
|
|
loadConfig(this, dataDirectory);
|
2022-07-08 02:47:50 +00:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
this.api = new RedisBungeeAPI(this);
|
2022-07-26 08:40:14 +00:00
|
|
|
InitialUtils.checkRedisVersion(this);
|
2022-07-17 05:51:24 +00:00
|
|
|
// check if this proxy is recovering from a crash and start heart the beat.
|
2022-07-26 06:58:00 +00:00
|
|
|
InitialUtils.checkIfRecovering(this, getDataFolder());
|
2022-07-08 02:47:50 +00:00
|
|
|
uuidTranslator = new UUIDTranslator(this);
|
|
|
|
dataManager = new VelocityDataManager(this);
|
|
|
|
psl = new PubSubListener(this);
|
|
|
|
this.httpClient = new OkHttpClient();
|
|
|
|
Dispatcher dispatcher = new Dispatcher(Executors.newFixedThreadPool(6));
|
|
|
|
this.httpClient.setDispatcher(dispatcher);
|
|
|
|
NameFetcher.setHttpClient(httpClient);
|
|
|
|
UUIDFetcher.setHttpClient(httpClient);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public RedisBungeeConfiguration getConfiguration() {
|
|
|
|
return this.configuration;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCount() {
|
|
|
|
return this.globalPlayerCount.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Set<String> getLocalPlayersAsUuidStrings() {
|
|
|
|
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
|
|
|
for (Player player : getProxy().getAllPlayers()) {
|
|
|
|
builder.add(player.getUniqueId().toString());
|
|
|
|
}
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-07 22:39:05 +00:00
|
|
|
public AbstractDataManager<Player, ?, ?, ?> getDataManager() {
|
2022-07-06 22:24:08 +00:00
|
|
|
return this.dataManager;
|
|
|
|
}
|
|
|
|
|
2022-07-15 22:58:48 +00:00
|
|
|
@Override
|
2022-07-17 05:51:24 +00:00
|
|
|
public Summoner<?> getSummoner() {
|
2022-07-15 22:58:48 +00:00
|
|
|
return this.jedisSummoner;
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
@Override
|
2022-07-27 13:43:51 +00:00
|
|
|
public AbstractRedisBungeeAPI getAbstractRedisBungeeApi() {
|
2022-07-06 22:24:08 +00:00
|
|
|
return this.api;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public UUIDTranslator getUuidTranslator() {
|
|
|
|
return this.uuidTranslator;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-26 06:58:00 +00:00
|
|
|
public Multimap<String, UUID> serverToPlayersCache() {
|
2022-07-06 22:24:08 +00:00
|
|
|
try {
|
2022-07-26 06:58:00 +00:00
|
|
|
return this.serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, this::serversToPlayers);
|
2022-07-06 22:24:08 +00:00
|
|
|
} catch (ExecutionException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-17 11:38:00 +00:00
|
|
|
public List<String> getProxiesIds() {
|
|
|
|
return proxiesIds;
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public PubSubListener getPubSubListener() {
|
|
|
|
return this.psl;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void executeAsync(Runnable runnable) {
|
|
|
|
this.getProxy().getScheduler().buildTask(this, runnable).schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
|
2022-07-15 06:52:53 +00:00
|
|
|
this.getProxy().getScheduler().buildTask(this, runnable).delay(time, timeUnit).schedule();
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void callEvent(Object event) {
|
2022-07-15 06:52:53 +00:00
|
|
|
this.getProxy().getEventManager().fireAndForget(event);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOnlineMode() {
|
|
|
|
return this.getProxy().getConfiguration().isOnlineMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void logInfo(String msg) {
|
|
|
|
this.getLogger().info(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void logWarn(String msg) {
|
|
|
|
this.getLogger().warn(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void logFatal(String msg) {
|
|
|
|
this.getLogger().error(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Player getPlayer(UUID uuid) {
|
|
|
|
return this.getProxy().getPlayer(uuid).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Player getPlayer(String name) {
|
|
|
|
return this.getProxy().getPlayer(name).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public UUID getPlayerUUID(String player) {
|
|
|
|
return this.getProxy().getPlayer(player).map(Player::getUniqueId).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getPlayerName(UUID player) {
|
|
|
|
return this.getProxy().getPlayer(player).map(Player::getUsername).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getPlayerServerName(Player player) {
|
|
|
|
return player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isPlayerOnAServer(Player player) {
|
|
|
|
return player.getCurrentServer().isPresent();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public InetAddress getPlayerIp(Player player) {
|
|
|
|
return player.getRemoteAddress().getAddress();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-07 23:23:44 +00:00
|
|
|
public void initialize() {
|
2022-07-26 08:42:42 +00:00
|
|
|
updateProxiesIds();
|
2022-07-17 05:51:24 +00:00
|
|
|
// start heartbeat task
|
2022-07-20 09:21:24 +00:00
|
|
|
heartbeatTask = getProxy().getScheduler().buildTask(this, new HeartbeatTask(this, this.globalPlayerCount)).repeat(HeartbeatTask.INTERVAL, HeartbeatTask.REPEAT_INTERVAL_TIME_UNIT).schedule();
|
2022-07-08 02:47:50 +00:00
|
|
|
|
2022-07-14 23:05:53 +00:00
|
|
|
getProxy().getEventManager().register(this, new RedisBungeeVelocityListener(this, configuration.getExemptAddresses()));
|
2022-07-08 02:47:50 +00:00
|
|
|
getProxy().getEventManager().register(this, dataManager);
|
|
|
|
getProxy().getScheduler().buildTask(this, psl).schedule();
|
2022-07-26 06:58:00 +00:00
|
|
|
|
|
|
|
IntegrityCheckTask integrityCheckTask = new IntegrityCheckTask(this) {
|
2022-07-17 05:51:24 +00:00
|
|
|
@Override
|
2022-07-26 13:47:18 +00:00
|
|
|
public void handlePlatformPlayer(String player, UnifiedJedis unifiedJedis) {
|
2022-07-26 06:58:00 +00:00
|
|
|
Player playerProxied = getProxy().getPlayer(UUID.fromString(player)).orElse(null);
|
|
|
|
if (playerProxied == null)
|
|
|
|
return; // We'll deal with it later.
|
2022-07-26 13:47:18 +00:00
|
|
|
VelocityPlayerUtils.createPlayer(playerProxied, unifiedJedis, false);
|
2022-07-08 02:47:50 +00:00
|
|
|
}
|
2022-07-17 05:51:24 +00:00
|
|
|
};
|
2022-07-26 06:58:00 +00:00
|
|
|
integrityCheck = getProxy().getScheduler().buildTask(this, integrityCheckTask::execute).repeat(30, TimeUnit.SECONDS).schedule();
|
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
|
2022-07-15 06:52:53 +00:00
|
|
|
// register plugin messages
|
|
|
|
IDENTIFIERS.forEach(getProxy().getChannelRegistrar()::register);
|
2022-07-06 22:24:08 +00:00
|
|
|
|
2022-07-22 12:10:06 +00:00
|
|
|
// register legacy commands
|
|
|
|
if (configuration.doRegisterLegacyCommands()) {
|
|
|
|
// Override Velocity commands
|
|
|
|
if (configuration.doOverrideBungeeCommands()) {
|
|
|
|
getProxy().getCommandManager().register("glist", new RedisBungeeCommands.GlistCommand(this), "redisbungee", "rglist");
|
|
|
|
}
|
|
|
|
getProxy().getCommandManager().register("sendtoall", new RedisBungeeCommands.SendToAll(this), "rsendtoall");
|
|
|
|
getProxy().getCommandManager().register("serverid", new RedisBungeeCommands.ServerId(this), "rserverid");
|
|
|
|
getProxy().getCommandManager().register("serverids", new RedisBungeeCommands.ServerIds(this));
|
|
|
|
getProxy().getCommandManager().register("pproxy", new RedisBungeeCommands.PlayerProxyCommand(this));
|
|
|
|
getProxy().getCommandManager().register("plist", new RedisBungeeCommands.PlistCommand(this), "rplist");
|
|
|
|
getProxy().getCommandManager().register("lastseen", new RedisBungeeCommands.LastSeenCommand(this), "rlastseen");
|
|
|
|
getProxy().getCommandManager().register("ip", new RedisBungeeCommands.IpCommand(this), "playerip", "rip", "rplayerip");
|
|
|
|
getProxy().getCommandManager().register("find", new RedisBungeeCommands.FindCommand(this), "rfind");
|
2022-07-15 06:52:53 +00:00
|
|
|
}
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void stop() {
|
2022-07-17 05:51:24 +00:00
|
|
|
// Poison the PubSub listener
|
|
|
|
if (psl != null) {
|
2022-07-06 22:24:08 +00:00
|
|
|
psl.poison();
|
2022-07-17 05:51:24 +00:00
|
|
|
}
|
|
|
|
if (integrityCheck != null) {
|
2022-07-06 22:24:08 +00:00
|
|
|
integrityCheck.cancel();
|
2022-07-17 05:51:24 +00:00
|
|
|
}
|
|
|
|
if (heartbeatTask != null) {
|
2022-07-06 22:24:08 +00:00
|
|
|
heartbeatTask.cancel();
|
2022-07-17 05:51:24 +00:00
|
|
|
}
|
2022-07-26 06:58:00 +00:00
|
|
|
ShutdownUtils.shutdownCleanup(this);
|
2022-07-17 05:51:24 +00:00
|
|
|
try {
|
|
|
|
this.jedisSummoner.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
2022-07-17 05:51:24 +00:00
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
this.httpClient.getDispatcher().getExecutorService().shutdown();
|
|
|
|
try {
|
|
|
|
this.httpClient.getDispatcher().getExecutorService().awaitTermination(20, TimeUnit.SECONDS);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-25 12:49:57 +00:00
|
|
|
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
|
|
|
|
this.jedisSummoner = summoner;
|
|
|
|
this.configuration = configuration;
|
|
|
|
this.redisBungeeMode = mode;
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 11:30:45 +00:00
|
|
|
|
2022-07-17 05:51:24 +00:00
|
|
|
@Override
|
|
|
|
public RedisBungeeMode getRedisBungeeMode() {
|
|
|
|
return this.redisBungeeMode;
|
|
|
|
}
|
|
|
|
|
2022-07-20 09:21:24 +00:00
|
|
|
@Override
|
2022-07-26 08:42:42 +00:00
|
|
|
public void updateProxiesIds() {
|
2022-07-26 06:58:00 +00:00
|
|
|
this.proxiesIds = this.getCurrentProxiesIds(false);
|
2022-07-20 09:21:24 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 22:24:08 +00:00
|
|
|
@Subscribe
|
|
|
|
public void proxyInit(ProxyInitializeEvent event) {
|
2022-07-07 23:23:44 +00:00
|
|
|
initialize();
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
public void proxyShutdownEvent(ProxyShutdownEvent event) {
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@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-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-20 08:32:04 +00:00
|
|
|
public Object createPlayerJoinedNetworkEvent(UUID uuid) {
|
|
|
|
return new PlayerJoinedNetworkEvent(uuid);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-20 08:32:04 +00:00
|
|
|
public Object createPlayerLeftNetworkEvent(UUID uuid) {
|
|
|
|
return new PlayerLeftNetworkEvent(uuid);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-07-20 08:32:04 +00:00
|
|
|
public Object createPubSubEvent(String channel, String message) {
|
|
|
|
return new PubSubMessageEvent(channel, message);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public ProxyServer getProxy() {
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Logger getLogger() {
|
|
|
|
return logger;
|
|
|
|
}
|
|
|
|
|
2022-07-15 06:52:53 +00:00
|
|
|
public Path getDataFolder() {
|
2022-07-06 22:24:08 +00:00
|
|
|
return this.dataFolder;
|
|
|
|
}
|
|
|
|
|
2022-07-22 11:12:32 +00:00
|
|
|
public InputStream getResourceAsStream(String name) {
|
|
|
|
return this.getClass().getClassLoader().getResourceAsStream(name);
|
2022-07-06 22:24:08 +00:00
|
|
|
}
|
|
|
|
}
|