mirror of
https://github.com/proxiodev/RedisBungee.git
synced 2026-04-09 08:30:26 +00:00
0.12.0 (#86)
## NOTES data system shouldn't effect anybody, unless you do any direct query to Redis query data, you should adapt the changes, by viewing classes `ProxyDataManager` and `PlayerDataManager` # Changes * RedisBungee is compiled with `java 17` now, Due java 11 support is ending at end of September * config version is now `2` which will reset your config if older version * Adventure API is included inside RedisBungee API * new Language infrastructure for RedisBungee built-in messages #85 *commands not included yet* * New data system which replaces Redis PubSub with Redis Streams *see below* * Ability to connect player to last server they where on using an config option #84 * new environment variable `REDISBUNGEE_PROXY_ID` which can be set before launch * new environment variable `REDISBUNGEE_NETWORK_ID` which can be set before launch * RedisBungee requires redis version 6.2 or above #88 * Better command system https://github.com/ProxioDev/RedisBungee/issues/93 ## New data system Due limitation of Redis PubSub in Cluster environment, Internals of RedisBungee were changed to support Redis Streams - Network Ids - networks ids used to group network proxies - example having 'test' network and 'main' network - Networks in the same redis server / cluster share the same UUID cache - Heartbeat system: - RedisBungee old heartbeat system used hastset on redisbungee to store the current unix time of the proxy to check what every proxy died or not, now instead we publish the heartbeat using unix time, and online count to proxy which proxy store it in their memory, which allow the `get number of online players` to be faster than pooling whole list in old data system. - PubSub - since redisbungee was initially designed with pubsub in mind, registration no longer required now for event to fire, see the api changes below. ## Commands System * rewritten using [acf lib](https://github.com/aikar/commands) to be platform independent * new command `/rb` or `/redisbungee` with sub commands `help`, `info`, 'clean', 'show'. * 'rb' * '/rb' and '/rb info'  * '/rb show'  * configuration to disable or override each command from legacy to new introduced one `/rb` ```yaml # For redis bungee legacy commands # either can be run using '/rbl glist' for example # or if 'install' is set to true '/glist' can be used. # 'install' also overrides the proxy installed commands # # In legacy commands each command got it own permissions since they had it own permission pre new command system, # so it's also applied to subcommands in '/rbl'. commands: # Permission redisbungee.legacy.use redisbungee-legacy: enabled: false subcommands: # Permission redisbungee.command.glist glist: enabled: false install: false # Permission redisbungee.command.find find: enabled: false install: false # Permission redisbungee.command.lastseen lastseen: enabled: false install: false # Permission redisbungee.command.ip ip: enabled: false install: false # Permission redisbungee.command.pproxy pproxy: enabled: false install: false # Permission redisbungee.command.sendtoall sendtoall: enabled: false install: false # Permission redisbungee.command.serverid serverid: enabled: false install: false # Permission redisbungee.command.serverids serverids: enabled: false install: false # Permission redisbungee.command.plist plist: enabled: false install: false # Permission redisbungee.command.use redisbungee: enabled: true ``` ## API changes - Kick api Deprecated: - `kickPlayer(String playerName, String message) ` - `kickPlayer(UUID playerUUID, String message) ` - newer where added using adventure api: - `kickPlayer(String playerName, Component message) ` - `kickPlayer(UUID playerUUID, Component message) ` - PubSub registration api Deprecated: ```java /** * Register (a) PubSub channel(s), so that you may handle PubSubMessageEvent for it. * * @param channels the channels to register * @since 0.3 * @deprecated No longer required */ @Deprecated public final void registerPubSubChannels(String... channels) { } /** * Unregister (a) PubSub channel(s). * * @param channels the channels to unregister * @since 0.3 * @deprecated No longer required */ @Deprecated public final void unregisterPubSubChannels(String... channels) { } ``` # Contributors * `summoncraft.us` for running this branch in production * @SrBedrock for providing [Brazilian Portuguese](https://en.wikipedia.org/wiki/Brazilian_Portuguese) translation #87 # issues closes #84 closes #88 closes #92 closes #81 closes #93 --------- Signed-off-by: mohammed jasem alaajel <xrambad@gmail.com> Co-authored-by: ThiagoROX <51332006+SrBedrock@users.noreply.github.com>
This commit is contained in:
@@ -5,18 +5,17 @@ plugins {
|
||||
id("xyz.jpenilla.run-waterfall") version "2.0.0"
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // bungeecord
|
||||
}
|
||||
val bungeecordApiVersion = "1.19-R0.1-SNAPSHOT"
|
||||
dependencies {
|
||||
api(project(":RedisBungee-API"))
|
||||
compileOnly("net.md-5:bungeecord-api:$bungeecordApiVersion") {
|
||||
compileOnly(libs.platform.bungeecord) {
|
||||
exclude("com.google.guava", "guava")
|
||||
exclude("com.google.code.gson", "gson")
|
||||
exclude("net.kyori","adventure-api")
|
||||
}
|
||||
implementation(libs.adventure.platforms.bungeecord)
|
||||
implementation(libs.adventure.gson)
|
||||
implementation(libs.acf.bungeecord)
|
||||
implementation(project(":RedisBungee-Commands"))
|
||||
}
|
||||
|
||||
description = "RedisBungee Bungeecord implementation"
|
||||
@@ -40,11 +39,13 @@ tasks {
|
||||
options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
|
||||
}
|
||||
runWaterfall {
|
||||
waterfallVersion("1.19")
|
||||
waterfallVersion("1.20")
|
||||
environment["REDISBUNGEE_PROXY_ID"] = "bungeecord-1"
|
||||
environment["REDISBUNGEE_NETWORK_ID"] = "dev"
|
||||
}
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.release.set(8)
|
||||
options.release.set(17)
|
||||
}
|
||||
javadoc {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
@@ -73,6 +74,10 @@ tasks {
|
||||
relocate("com.google.gson", "com.imaginarycode.minecraft.redisbungee.internal.com.google.gson")
|
||||
relocate("com.google.j2objc", "com.imaginarycode.minecraft.redisbungee.internal.com.google.j2objc")
|
||||
relocate("com.google.thirdparty", "com.imaginarycode.minecraft.redisbungee.internal.com.google.thirdparty")
|
||||
relocate("com.github.benmanes.caffeine", "com.imaginarycode.minecraft.redisbungee.internal.caffeine")
|
||||
// acf shade
|
||||
relocate("co.aikar.commands", "com.imaginarycode.minecraft.redisbungee.internal.acf.commands")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import co.aikar.commands.BungeeCommandIssuer;
|
||||
import co.aikar.commands.CommandIssuer;
|
||||
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
|
||||
public class BungeeCommandPlatformHelper extends CommandPlatformHelper {
|
||||
|
||||
@Override
|
||||
public void sendMessage(CommandIssuer issuer, Component component) {
|
||||
BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer;
|
||||
bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.AbstractDataManager;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeDataManager extends AbstractDataManager<ProxiedPlayer, PostLoginEvent, PlayerDisconnectEvent, PubSubMessageEvent> implements Listener {
|
||||
|
||||
public BungeeDataManager(RedisBungeePlugin<ProxiedPlayer> plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPostLogin(PostLoginEvent event) {
|
||||
invalidate(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
invalidate(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPubSubMessage(PubSubMessageEvent event) {
|
||||
handlePubSubMessage(event.getChannel(), event.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKick(UUID target, String message) {
|
||||
// check if the player is online on this proxy
|
||||
ProxiedPlayer player = plugin.getPlayer(target);
|
||||
if (player == null) return false;
|
||||
player.disconnect(TextComponent.fromLegacyText(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer, PostLoginEvent, PlayerDisconnectEvent, PubSubMessageEvent, PlayerChangedServerNetworkEvent, PlayerLeftNetworkEvent, ServerConnectedEvent> implements Listener {
|
||||
|
||||
public BungeePlayerDataManager(RedisBungeePlugin<ProxiedPlayer> plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPlayerChangedServerNetworkEvent(PlayerChangedServerNetworkEvent event) {
|
||||
super.handleNetworkPlayerServerChange(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onNetworkPlayerQuit(PlayerLeftNetworkEvent event) {
|
||||
super.handleNetworkPlayerQuit(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPubSubMessageEvent(PubSubMessageEvent event) {
|
||||
super.handlePubSubMessageEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onServerConnectedEvent(ServerConnectedEvent event) {
|
||||
final String currentServer = event.getServer().getInfo().getName();
|
||||
final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
|
||||
super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLoginEvent(LoginEvent event) {
|
||||
event.registerIntent((Plugin) plugin);
|
||||
// check if online
|
||||
if (getLastOnline(event.getConnection().getUniqueId()) == 0) {
|
||||
if (plugin.configuration().kickWhenOnline()) {
|
||||
kickPlayer(event.getConnection().getUniqueId(), plugin.langConfiguration().messages().loggedInFromOtherLocation());
|
||||
// wait 3 seconds before releasing the event
|
||||
plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
|
||||
} else {
|
||||
event.setCancelled(true);
|
||||
event.setCancelReason(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().alreadyLoggedIn()));
|
||||
event.completeIntent((Plugin) plugin);
|
||||
}
|
||||
} else {
|
||||
event.completeIntent((Plugin) plugin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onLoginEvent(PostLoginEvent event) {
|
||||
super.addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getAddress().getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onDisconnectEvent(PlayerDisconnectEvent event) {
|
||||
super.removePlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import redis.clients.jedis.UnifiedJedis;
|
||||
public class BungeePlayerUtils {
|
||||
|
||||
public static void createBungeePlayer(ProxiedPlayer player, UnifiedJedis unifiedJedis, boolean fireEvent) {
|
||||
String serverName = null;
|
||||
if (player.getServer() != null) {
|
||||
serverName = player.getServer().getInfo().getName();
|
||||
}
|
||||
PendingConnection pendingConnection = player.getPendingConnection();
|
||||
PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, pendingConnection.getAddress().getAddress(), fireEvent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,131 +10,104 @@
|
||||
|
||||
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.imaginarycode.minecraft.redisbungee.api.config.ConfigLoader;
|
||||
import co.aikar.commands.BungeeCommandManager;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.config.LangConfiguration;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.LangConfigLoader;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.tasks.*;
|
||||
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils;
|
||||
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;
|
||||
import com.imaginarycode.minecraft.redisbungee.commands.CommandLoader;
|
||||
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.*;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
|
||||
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;
|
||||
import com.squareup.okhttp.Dispatcher;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
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 redis.clients.jedis.*;
|
||||
import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.util.*;
|
||||
import java.sql.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlayer>, ConfigLoader {
|
||||
public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlayer>, ConfigLoader, LangConfigLoader {
|
||||
|
||||
private static RedisBungeeAPI apiStatic;
|
||||
|
||||
private AbstractRedisBungeeAPI api;
|
||||
private RedisBungeeMode redisBungeeMode;
|
||||
private PubSubListener psl = null;
|
||||
private ProxyDataManager proxyDataManager;
|
||||
private BungeePlayerDataManager playerDataManager;
|
||||
private ScheduledTask heartbeatTask;
|
||||
private ScheduledTask cleanupTask;
|
||||
private Summoner<?> summoner;
|
||||
private UUIDTranslator uuidTranslator;
|
||||
private RedisBungeeConfiguration configuration;
|
||||
private BungeeDataManager dataManager;
|
||||
private LangConfiguration langConfiguration;
|
||||
private OkHttpClient httpClient;
|
||||
private volatile List<String> proxiesIds;
|
||||
private final AtomicInteger globalPlayerCount = new AtomicInteger();
|
||||
private Future<?> integrityCheck;
|
||||
private Future<?> heartbeatTask;
|
||||
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();
|
||||
private BungeeCommandManager commandManager;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger("RedisBungee");
|
||||
|
||||
|
||||
@Override
|
||||
public RedisBungeeConfiguration getConfiguration() {
|
||||
public RedisBungeeConfiguration configuration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return this.globalPlayerCount.get();
|
||||
public LangConfiguration langConfiguration() {
|
||||
return this.langConfiguration;
|
||||
}
|
||||
|
||||
@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 AbstractDataManager<ProxiedPlayer, ?, ?, ?> getDataManager() {
|
||||
return this.dataManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AbstractRedisBungeeAPI getAbstractRedisBungeeApi() {
|
||||
return this.api;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyDataManager proxyDataManager() {
|
||||
return this.proxyDataManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerDataManager<ProxiedPlayer, ?, ?, ?, ?, ?, ?> playerDataManager() {
|
||||
return this.playerDataManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUIDTranslator getUuidTranslator() {
|
||||
return this.uuidTranslator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multimap<String, UUID> serverToPlayersCache() {
|
||||
try {
|
||||
return this.serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, this::serversToPlayers);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getProxiesIds() {
|
||||
return proxiesIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PubSubListener getPubSubListener() {
|
||||
return this.psl;
|
||||
}
|
||||
|
||||
@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 fireEvent(Object event) {
|
||||
this.getProxy().getPluginManager().callEvent((Event) event);
|
||||
@@ -147,17 +120,32 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
|
||||
@Override
|
||||
public void logInfo(String msg) {
|
||||
this.getLogger().info(msg);
|
||||
this.logger.info(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(String format, Object... object) {
|
||||
this.logger.info(format, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarn(String msg) {
|
||||
this.getLogger().warning(msg);
|
||||
this.logger.warn(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarn(String format, Object... object) {
|
||||
this.logger.warn(format, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logFatal(String msg) {
|
||||
this.getLogger().severe(msg);
|
||||
this.logger.error(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logFatal(String format, Throwable throwable) {
|
||||
this.logger.error(format, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,6 +168,15 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
return this.getProxy().getPlayer(player).getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePlatformKick(UUID uuid, Component message) {
|
||||
ProxiedPlayer player = getPlayer(uuid);
|
||||
if (player == null) return false;
|
||||
if (!player.isConnected()) return false;
|
||||
player.disconnect(BungeeComponentSerializer.get().serialize(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerServerName(ProxiedPlayer player) {
|
||||
return player.getServer().getInfo().getName();
|
||||
@@ -199,6 +196,7 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
@Override
|
||||
public void initialize() {
|
||||
logInfo("Initializing RedisBungee.....");
|
||||
logInfo("Version: {}", Constants.VERSION);
|
||||
ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory();
|
||||
ScheduledExecutorService service = Executors.newScheduledThreadPool(24, factory);
|
||||
try {
|
||||
@@ -209,16 +207,39 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
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.....");
|
||||
getLogger().log(Level.WARNING, "skipping replacement.....");
|
||||
}
|
||||
try {
|
||||
loadConfig(this, getDataFolder());
|
||||
loadConfig(this, getDataFolder().toPath());
|
||||
loadLangConfig(this, getDataFolder().toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to load/save config", e);
|
||||
}
|
||||
// init the api class
|
||||
this.api = new RedisBungeeAPI(this);
|
||||
apiStatic = (RedisBungeeAPI) this.api;
|
||||
// init the proxy data manager
|
||||
this.proxyDataManager = new ProxyDataManager(this) {
|
||||
@Override
|
||||
public Set<UUID> getLocalOnlineUUIDs() {
|
||||
HashSet<UUID> uuids = new HashSet<>();
|
||||
ProxyServer.getInstance().getPlayers().forEach((proxiedPlayer) -> uuids.add(proxiedPlayer.getUniqueId()));
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handlePlatformCommandExecution(String command) {
|
||||
logInfo("Dispatching {}", command);
|
||||
ProxyServer.getInstance().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
|
||||
}
|
||||
};
|
||||
this.playerDataManager = new BungeePlayerDataManager(this);
|
||||
|
||||
getProxy().getPluginManager().registerListener(this, this.playerDataManager);
|
||||
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this));
|
||||
// start listening
|
||||
getProxy().getScheduler().runAsync(this, proxyDataManager);
|
||||
// heartbeat
|
||||
this.heartbeatTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.publishHeartbeat(), 0, 1, TimeUnit.SECONDS);
|
||||
// cleanup
|
||||
this.cleanupTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.correctionTask(), 0, 60, TimeUnit.SECONDS);
|
||||
// init the http lib
|
||||
httpClient = new OkHttpClient();
|
||||
Dispatcher dispatcher = new Dispatcher(getExecutorService());
|
||||
@@ -226,71 +247,49 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
NameFetcher.setHttpClient(httpClient);
|
||||
UUIDFetcher.setHttpClient(httpClient);
|
||||
InitialUtils.checkRedisVersion(this);
|
||||
// check if this proxy is recovering from a crash and start heart the beat.
|
||||
InitialUtils.checkIfRecovering(this, getDataFolder().toPath());
|
||||
updateProxiesIds();
|
||||
uuidTranslator = new UUIDTranslator(this);
|
||||
heartbeatTask = service.scheduleAtFixedRate(new HeartbeatTask(this, this.globalPlayerCount), 0, HeartbeatTask.INTERVAL, HeartbeatTask.REPEAT_INTERVAL_TIME_UNIT);
|
||||
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);
|
||||
|
||||
IntegrityCheckTask integrityCheckTask = new IntegrityCheckTask(this) {
|
||||
@Override
|
||||
public void handlePlatformPlayer(String player, UnifiedJedis unifiedJedis) {
|
||||
ProxiedPlayer proxiedPlayer = ProxyServer.getInstance().getPlayer(UUID.fromString(player));
|
||||
if (proxiedPlayer == null)
|
||||
return; // We'll deal with it later.
|
||||
|
||||
BungeePlayerUtils.createBungeePlayer(proxiedPlayer, unifiedJedis, false);
|
||||
}
|
||||
};
|
||||
|
||||
integrityCheck = service.scheduleAtFixedRate(integrityCheckTask::execute, 0, IntegrityCheckTask.INTERVAL, IntegrityCheckTask.TIMEUNIT);
|
||||
|
||||
// register plugin messages channel.
|
||||
getProxy().registerChannel("legacy:redisbungee");
|
||||
getProxy().registerChannel("RedisBungee");
|
||||
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));
|
||||
}
|
||||
|
||||
// init the api
|
||||
this.api = new RedisBungeeAPI(this);
|
||||
apiStatic = (RedisBungeeAPI) this.api;
|
||||
|
||||
// commands
|
||||
CommandPlatformHelper.init(new BungeeCommandPlatformHelper());
|
||||
this.commandManager = new BungeeCommandManager(this);
|
||||
CommandLoader.initCommands(this.commandManager, this);
|
||||
|
||||
logInfo("RedisBungee initialized successfully ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
logInfo("Turning off redis connections.....");
|
||||
// Poison the PubSub listener
|
||||
if (psl != null) {
|
||||
psl.poison();
|
||||
}
|
||||
if (integrityCheck != null) {
|
||||
integrityCheck.cancel(true);
|
||||
getProxy().getPluginManager().unregisterListeners(this);
|
||||
|
||||
if (this.cleanupTask != null) {
|
||||
this.cleanupTask.cancel();
|
||||
}
|
||||
if (heartbeatTask != null) {
|
||||
heartbeatTask.cancel(true);
|
||||
heartbeatTask.cancel();
|
||||
}
|
||||
try {
|
||||
this.proxyDataManager.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
getProxy().getPluginManager().unregisterListeners(this);
|
||||
ShutdownUtils.shutdownCleanup(this);
|
||||
try {
|
||||
this.summoner.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
logInfo("RedisBungee shutdown");
|
||||
if (this.commandManager != null) {
|
||||
this.commandManager.unregisterCommands();
|
||||
}
|
||||
logInfo("RedisBungee shutdown successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,10 +302,14 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
return this.redisBungeeMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeAsync(Runnable runnable) {
|
||||
this.getProxy().getScheduler().runAsync(this, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProxiesIds() {
|
||||
proxiesIds = getCurrentProxiesIds(false);
|
||||
public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
|
||||
this.getProxy().getScheduler().schedule(this, runnable, time, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -349,9 +352,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
/**
|
||||
* This returns an instance of {@link RedisBungeeAPI}
|
||||
*
|
||||
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for old plugins that no longer updated.
|
||||
*
|
||||
* @return the {@link AbstractRedisBungeeAPI} object instance.
|
||||
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for old plugins that no longer updated.
|
||||
*/
|
||||
@Deprecated
|
||||
public static RedisBungeeAPI getApi() {
|
||||
@@ -362,4 +364,10 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
|
||||
public JedisPool getPool() {
|
||||
return api.getJedisPool();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLangConfigLoad(LangConfiguration langConfiguration) {
|
||||
this.langConfiguration = langConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.AbstractRedisBungeeListener;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.util.player.PlayerUtils;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.util.payload.PayloadUtils;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.event.*;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import redis.clients.jedis.UnifiedJedis;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.*;
|
||||
|
||||
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultimap;
|
||||
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultiset;
|
||||
import static net.md_5.bungee.event.EventPriority.HIGHEST;
|
||||
|
||||
public class RedisBungeeBungeeListener extends AbstractRedisBungeeListener<LoginEvent, PostLoginEvent, PlayerDisconnectEvent, ServerConnectedEvent, ProxyPingEvent, PluginMessageEvent, PubSubMessageEvent> implements Listener {
|
||||
|
||||
|
||||
public RedisBungeeBungeeListener(RedisBungeePlugin<?> plugin, List<InetAddress> exemptAddresses) {
|
||||
super(plugin, exemptAddresses);
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler(priority = HIGHEST)
|
||||
public void onLogin(LoginEvent event) {
|
||||
event.registerIntent((Plugin) plugin);
|
||||
plugin.executeAsync(new RedisTask<Void>(plugin) {
|
||||
@Override
|
||||
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
||||
try {
|
||||
if (event.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
if (plugin.getConfiguration().restoreOldKickBehavior()) {
|
||||
for (String s : plugin.getProxiesIds()) {
|
||||
if (unifiedJedis.sismember("proxy:" + s + ":usersOnline", event.getConnection().getUniqueId().toString())) {
|
||||
event.setCancelled(true);
|
||||
event.setCancelReason(plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (api.isPlayerOnline(event.getConnection().getUniqueId())) {
|
||||
PlayerUtils.setKickedOtherLocation(event.getConnection().getUniqueId().toString(), unifiedJedis);
|
||||
api.kickPlayer(event.getConnection().getUniqueId(), plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
event.completeIntent((Plugin) plugin);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPostLogin(PostLoginEvent event) {
|
||||
plugin.executeAsync(new RedisTask<Void>(plugin) {
|
||||
@Override
|
||||
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
||||
plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), unifiedJedis);
|
||||
BungeePlayerUtils.createBungeePlayer(event.getPlayer(), unifiedJedis, true);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
plugin.executeAsync(new RedisTask<Void>(plugin) {
|
||||
@Override
|
||||
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
||||
PlayerUtils.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), unifiedJedis, true);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onServerChange(ServerConnectedEvent event) {
|
||||
final String currentServer = event.getServer().getInfo().getName();
|
||||
final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
|
||||
plugin.executeAsync(new RedisTask<Void>(plugin) {
|
||||
@Override
|
||||
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
|
||||
unifiedJedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName());
|
||||
PayloadUtils.playerServerChangePayload(event.getPlayer().getUniqueId(), unifiedJedis, currentServer, oldServer);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPing(ProxyPingEvent event) {
|
||||
if (exemptAddresses.contains(event.getConnection().getAddress().getAddress())) {
|
||||
return;
|
||||
}
|
||||
ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection());
|
||||
|
||||
if (forced != null && event.getConnection().getListener().isPingPassthrough()) {
|
||||
return;
|
||||
}
|
||||
event.getResponse().getPlayers().setOnline(plugin.getCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler
|
||||
public void onPluginMessage(PluginMessageEvent event) {
|
||||
if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) {
|
||||
final String currentChannel = event.getTag();
|
||||
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
|
||||
plugin.executeAsync(() -> {
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(data);
|
||||
|
||||
String subchannel = in.readUTF();
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
String type;
|
||||
|
||||
switch (subchannel) {
|
||||
case "PlayerList":
|
||||
out.writeUTF("PlayerList");
|
||||
Set<UUID> original = Collections.emptySet();
|
||||
type = in.readUTF();
|
||||
if (type.equals("ALL")) {
|
||||
out.writeUTF("ALL");
|
||||
original = plugin.getPlayers();
|
||||
} else {
|
||||
out.writeUTF(type);
|
||||
try {
|
||||
original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
Set<String> players = new HashSet<>();
|
||||
for (UUID uuid : original)
|
||||
players.add(plugin.getUuidTranslator().getNameFromUuid(uuid, false));
|
||||
out.writeUTF(Joiner.on(',').join(players));
|
||||
break;
|
||||
case "PlayerCount":
|
||||
out.writeUTF("PlayerCount");
|
||||
type = in.readUTF();
|
||||
if (type.equals("ALL")) {
|
||||
out.writeUTF("ALL");
|
||||
out.writeInt(plugin.getCount());
|
||||
} else {
|
||||
out.writeUTF(type);
|
||||
try {
|
||||
out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
|
||||
} catch (IllegalArgumentException e) {
|
||||
out.writeInt(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "LastOnline":
|
||||
String user = in.readUTF();
|
||||
out.writeUTF("LastOnline");
|
||||
out.writeUTF(user);
|
||||
out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
|
||||
break;
|
||||
case "ServerPlayers":
|
||||
String type1 = in.readUTF();
|
||||
out.writeUTF("ServerPlayers");
|
||||
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
|
||||
|
||||
boolean includesUsers;
|
||||
|
||||
switch (type1) {
|
||||
case "COUNT":
|
||||
includesUsers = false;
|
||||
break;
|
||||
case "PLAYERS":
|
||||
includesUsers = true;
|
||||
break;
|
||||
default:
|
||||
// TODO: Should I raise an error?
|
||||
return;
|
||||
}
|
||||
|
||||
out.writeUTF(type1);
|
||||
|
||||
if (includesUsers) {
|
||||
Multimap<String, String> human = HashMultimap.create();
|
||||
for (Map.Entry<String, UUID> entry : multimap.entries()) {
|
||||
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
|
||||
}
|
||||
serializeMultimap(human, true, out);
|
||||
} else {
|
||||
serializeMultiset(multimap.keys(), out);
|
||||
}
|
||||
break;
|
||||
case "Proxy":
|
||||
out.writeUTF("Proxy");
|
||||
out.writeUTF(plugin.getConfiguration().getProxyId());
|
||||
break;
|
||||
case "PlayerProxy":
|
||||
String username = in.readUTF();
|
||||
out.writeUTF("PlayerProxy");
|
||||
out.writeUTF(username);
|
||||
out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@EventHandler
|
||||
public void onPubSubMessage(PubSubMessageEvent event) {
|
||||
if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + plugin.getAbstractRedisBungeeApi().getProxyId())) {
|
||||
String message = event.getMessage();
|
||||
if (message.startsWith("/"))
|
||||
message = message.substring(1);
|
||||
plugin.logInfo("Invoking command via PubSub: /" + message);
|
||||
((Plugin) plugin).getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
|
||||
|
||||
public class RedisBungeeListener implements Listener {
|
||||
|
||||
private final RedisBungeePlugin<ProxiedPlayer> plugin;
|
||||
|
||||
public RedisBungeeListener(RedisBungeePlugin<ProxiedPlayer> plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPing(ProxyPingEvent event) {
|
||||
if (!plugin.configuration().handleMotd()) return;
|
||||
if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getAddress().getAddress())) return;
|
||||
ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection());
|
||||
|
||||
if (forced != null && event.getConnection().getListener().isPingPassthrough()) return;
|
||||
event.getResponse().getPlayers().setOnline(plugin.proxyDataManager().totalNetworkPlayers());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler
|
||||
public void onPluginMessage(PluginMessageEvent event) {
|
||||
if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) {
|
||||
final String currentChannel = event.getTag();
|
||||
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
|
||||
plugin.executeAsync(() -> {
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(data);
|
||||
|
||||
String subchannel = in.readUTF();
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
String type;
|
||||
|
||||
switch (subchannel) {
|
||||
case "PlayerList" -> {
|
||||
out.writeUTF("PlayerList");
|
||||
Set<UUID> original = Collections.emptySet();
|
||||
type = in.readUTF();
|
||||
if (type.equals("ALL")) {
|
||||
out.writeUTF("ALL");
|
||||
original = plugin.proxyDataManager().networkPlayers();
|
||||
} else {
|
||||
out.writeUTF(type);
|
||||
try {
|
||||
original = plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
Set<String> players = new HashSet<>();
|
||||
for (UUID uuid : original)
|
||||
players.add(plugin.getUuidTranslator().getNameFromUuid(uuid, false));
|
||||
out.writeUTF(Joiner.on(',').join(players));
|
||||
}
|
||||
case "PlayerCount" -> {
|
||||
out.writeUTF("PlayerCount");
|
||||
type = in.readUTF();
|
||||
if (type.equals("ALL")) {
|
||||
out.writeUTF("ALL");
|
||||
out.writeInt(plugin.proxyDataManager().totalNetworkPlayers());
|
||||
} else {
|
||||
out.writeUTF(type);
|
||||
try {
|
||||
out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
|
||||
} catch (IllegalArgumentException e) {
|
||||
out.writeInt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
case "LastOnline" -> {
|
||||
String user = in.readUTF();
|
||||
out.writeUTF("LastOnline");
|
||||
out.writeUTF(user);
|
||||
out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true))));
|
||||
}
|
||||
case "ServerPlayers" -> {
|
||||
String type1 = in.readUTF();
|
||||
out.writeUTF("ServerPlayers");
|
||||
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
|
||||
boolean includesUsers;
|
||||
switch (type1) {
|
||||
case "COUNT" -> includesUsers = false;
|
||||
case "PLAYERS" -> includesUsers = true;
|
||||
default -> {
|
||||
// TODO: Should I raise an error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
out.writeUTF(type1);
|
||||
if (includesUsers) {
|
||||
Multimap<String, String> human = HashMultimap.create();
|
||||
for (Map.Entry<String, UUID> entry : multimap.entries()) {
|
||||
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
|
||||
}
|
||||
serializeMultimap(human, true, out);
|
||||
} else {
|
||||
serializeMultiset(multimap.keys(), out);
|
||||
}
|
||||
}
|
||||
case "Proxy" -> {
|
||||
out.writeUTF("Proxy");
|
||||
out.writeUTF(plugin.configuration().getProxyId());
|
||||
}
|
||||
case "PlayerProxy" -> {
|
||||
String username = in.readUTF();
|
||||
out.writeUTF("PlayerProxy");
|
||||
out.writeUTF(username);
|
||||
out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true))));
|
||||
}
|
||||
default -> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
((Server) event.getSender()).sendData(currentChannel, out.toByteArray());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerConnectEvent(ServerConnectEvent event) {
|
||||
if (event.getReason() == ServerConnectEvent.Reason.JOIN_PROXY && plugin.configuration().handleReconnectToLastServer()) {
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
String lastServer = plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
|
||||
if (lastServer == null) return;
|
||||
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverConnecting(player.getLocale(), lastServer)));
|
||||
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(lastServer);
|
||||
if (serverInfo == null) {
|
||||
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverNotFound(player.getLocale(), lastServer)));
|
||||
return;
|
||||
}
|
||||
event.setTarget(serverInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,353 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-present RedisBungee contributors
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
*
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.imaginarycode.minecraft.redisbungee.commands;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
|
||||
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
|
||||
* <p>
|
||||
* All classes use the {@link AbstractRedisBungeeAPI}.
|
||||
*
|
||||
* @author tuxed
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public class RedisBungeeCommands {
|
||||
private static final BaseComponent[] NO_PLAYER_SPECIFIED =
|
||||
new ComponentBuilder("You must specify a player name.").color(ChatColor.RED).create();
|
||||
private static final BaseComponent[] PLAYER_NOT_FOUND =
|
||||
new ComponentBuilder("No such player found.").color(ChatColor.RED).create();
|
||||
private static final BaseComponent[] NO_COMMAND_SPECIFIED =
|
||||
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
|
||||
|
||||
private static String playerPlural(int num) {
|
||||
return num == 1 ? num + " player is" : num + " players are";
|
||||
}
|
||||
|
||||
public static class GlistCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public GlistCommand(RedisBungee plugin) {
|
||||
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int count = plugin.getAbstractRedisBungeeApi().getPlayerCount();
|
||||
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
|
||||
.append(playerPlural(count) + " currently online.").create();
|
||||
if (args.length > 0 && args[0].equals("showall")) {
|
||||
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
|
||||
Multimap<String, String> human = HashMultimap.create();
|
||||
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
|
||||
// if for any reason UUID translation fails just return the uuid as name, to make command finish executing.
|
||||
String playerName = plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false);
|
||||
human.put(entry.getKey(), playerName != null ? playerName : entry.getValue().toString());
|
||||
}
|
||||
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
|
||||
TextComponent serverName = new TextComponent();
|
||||
serverName.setColor(ChatColor.GREEN);
|
||||
serverName.setText("[" + server + "] ");
|
||||
TextComponent serverCount = new TextComponent();
|
||||
serverCount.setColor(ChatColor.YELLOW);
|
||||
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
|
||||
TextComponent serverPlayers = new TextComponent();
|
||||
serverPlayers.setColor(ChatColor.WHITE);
|
||||
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
|
||||
sender.sendMessage(serverName, serverCount, serverPlayers);
|
||||
}
|
||||
sender.sendMessage(playersOnline);
|
||||
} else {
|
||||
sender.sendMessage(playersOnline);
|
||||
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class FindCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public FindCommand(RedisBungee plugin) {
|
||||
super("find", "bungeecord.command.find", "rfind");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (args.length > 0) {
|
||||
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
ServerInfo si = plugin.getProxy().getServerInfo(plugin.getAbstractRedisBungeeApi().getServerNameFor(uuid));
|
||||
if (si != null) {
|
||||
TextComponent message = new TextComponent();
|
||||
message.setColor(ChatColor.BLUE);
|
||||
message.setText(args[0] + " is on " + si.getName() + ".");
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class LastSeenCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public LastSeenCommand(RedisBungee plugin) {
|
||||
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (args.length > 0) {
|
||||
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
long secs = plugin.getAbstractRedisBungeeApi().getLastOnline(uuid);
|
||||
TextComponent message = new TextComponent();
|
||||
if (secs == 0) {
|
||||
message.setColor(ChatColor.GREEN);
|
||||
message.setText(args[0] + " is currently online.");
|
||||
} else if (secs != -1) {
|
||||
message.setColor(ChatColor.BLUE);
|
||||
message.setText(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
|
||||
} else {
|
||||
message.setColor(ChatColor.RED);
|
||||
message.setText(args[0] + " has never been online.");
|
||||
}
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class IpCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public IpCommand(RedisBungee plugin) {
|
||||
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (args.length > 0) {
|
||||
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
|
||||
if (ia != null) {
|
||||
TextComponent message = new TextComponent();
|
||||
message.setColor(ChatColor.GREEN);
|
||||
message.setText(args[0] + " is connected from " + ia.toString() + ".");
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerProxyCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public PlayerProxyCommand(RedisBungee plugin) {
|
||||
super("pproxy", "redisbungee.command.pproxy");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (args.length > 0) {
|
||||
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
|
||||
if (proxy != null) {
|
||||
TextComponent message = new TextComponent();
|
||||
message.setColor(ChatColor.GREEN);
|
||||
message.setText(args[0] + " is connected to " + proxy + ".");
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
sender.sendMessage(PLAYER_NOT_FOUND);
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(NO_PLAYER_SPECIFIED);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class SendToAll extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public SendToAll(RedisBungee plugin) {
|
||||
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (args.length > 0) {
|
||||
String command = Joiner.on(" ").skipNulls().join(args);
|
||||
plugin.getAbstractRedisBungeeApi().sendProxyCommand(command);
|
||||
TextComponent message = new TextComponent();
|
||||
message.setColor(ChatColor.GREEN);
|
||||
message.setText("Sent the command /" + command + " to all proxies.");
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
sender.sendMessage(NO_COMMAND_SPECIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ServerId extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public ServerId(RedisBungee plugin) {
|
||||
super("serverid", "redisbungee.command.serverid", "rserverid");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
TextComponent textComponent = new TextComponent();
|
||||
textComponent.setText("You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".");
|
||||
textComponent.setColor(ChatColor.YELLOW);
|
||||
sender.sendMessage(textComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ServerIds extends Command {
|
||||
private final RedisBungee plugin;
|
||||
public ServerIds(RedisBungee plugin) {
|
||||
super("serverids", "redisbungee.command.serverids");
|
||||
this.plugin =plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] strings) {
|
||||
TextComponent textComponent = new TextComponent();
|
||||
textComponent.setText("All server IDs: " + Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()));
|
||||
textComponent.setColor(ChatColor.YELLOW);
|
||||
sender.sendMessage(textComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlistCommand extends Command {
|
||||
private final RedisBungee plugin;
|
||||
|
||||
public PlistCommand(RedisBungee plugin) {
|
||||
super("plist", "redisbungee.command.plist", "rplist");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getProxyId();
|
||||
if (!plugin.getProxiesIds().contains(proxy)) {
|
||||
sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
|
||||
return;
|
||||
}
|
||||
Set<UUID> players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy);
|
||||
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
|
||||
.append(playerPlural(players.size()) + " currently on proxy " + proxy + ".").create();
|
||||
if (args.length >= 2 && args[1].equals("showall")) {
|
||||
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
|
||||
Multimap<String, String> human = HashMultimap.create();
|
||||
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
|
||||
if (players.contains(entry.getValue())) {
|
||||
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
|
||||
}
|
||||
}
|
||||
for (String server : new TreeSet<>(human.keySet())) {
|
||||
TextComponent serverName = new TextComponent();
|
||||
serverName.setColor(ChatColor.RED);
|
||||
serverName.setText("[" + server + "] ");
|
||||
TextComponent serverCount = new TextComponent();
|
||||
serverCount.setColor(ChatColor.YELLOW);
|
||||
serverCount.setText("(" + human.get(server).size() + "): ");
|
||||
TextComponent serverPlayers = new TextComponent();
|
||||
serverPlayers.setColor(ChatColor.WHITE);
|
||||
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
|
||||
sender.sendMessage(serverName, serverCount, serverPlayers);
|
||||
}
|
||||
sender.sendMessage(playersOnline);
|
||||
} else {
|
||||
sender.sendMessage(playersOnline);
|
||||
sender.sendMessage(new ComponentBuilder("To see all players online, use /plist " + proxy + " showall.").color(ChatColor.YELLOW).create());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user