2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-04-08 16:10:26 +00:00

new data system

This commit is contained in:
2024-04-12 22:37:02 +04:00
parent 006066f66c
commit 8aaae6702e
57 changed files with 1967 additions and 2116 deletions

View File

@@ -39,7 +39,7 @@ tasks {
"https://jd.papermc.io/velocity/3.0.0/", // velocity api
)
val apiDocs = File(rootProject.projectDir, "RedisBungee-API/build/docs/javadoc")
options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
options.linksOffline("https://ci.limework.net/RedisBungee/RedisBungee-API/build/docs/javadoc", apiDocs.path)
}
runVelocity {
velocityVersion("3.3.0-SNAPSHOT")
@@ -61,6 +61,7 @@ tasks {
relocate("com.squareup.okhttp", "com.imaginarycode.minecraft.redisbungee.internal.okhttp")
relocate("okio", "com.imaginarycode.minecraft.redisbungee.internal.okio")
relocate("org.json", "com.imaginarycode.minecraft.redisbungee.internal.json")
relocate("com.github.benmanes.caffeine", "com.imaginarycode.minecraft.redisbungee.internal.caffeine")
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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 com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.ServerPing;
import java.util.*;
import java.util.stream.Collectors;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultimap;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultiset;
public class RedisBungeeListener {
private final RedisBungeePlugin<Player> plugin;
public RedisBungeeListener(RedisBungeePlugin<Player> plugin) {
this.plugin = plugin;
}
@Subscribe(order = PostOrder.LAST) // some plugins changes it online players so we need to be executed as last
public void onPing(ProxyPingEvent event) {
if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getRemoteAddress().getAddress())) {
return;
}
ServerPing.Builder ping = event.getPing().asBuilder();
ping.onlinePlayers(plugin.proxyDataManager().totalNetworkPlayers());
event.setPing(ping.build());
}
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
if (!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
return;
}
event.setResult(PluginMessageEvent.ForwardResult.handled());
plugin.executeAsync(() -> {
ByteArrayDataInput in = event.dataAsDataStream();
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 = original.stream()
.map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
.collect(Collectors.toSet());
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.proxyDataManager().totalNetworkPlayers());
} 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;
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);
}
break;
case "Proxy":
out.writeUTF("Proxy");
out.writeUTF(plugin.configuration().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;
}
((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
});
}
}

View File

@@ -1,263 +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 com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent.ForwardResult;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.ServerPing;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import redis.clients.jedis.UnifiedJedis;
import java.net.InetAddress;
import java.util.*;
import java.util.stream.Collectors;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultimap;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.Serializations.serializeMultiset;
public class RedisBungeeVelocityListener extends AbstractRedisBungeeListener<LoginEvent, PostLoginEvent, DisconnectEvent, ServerConnectedEvent, ProxyPingEvent, PluginMessageEvent, PubSubMessageEvent> {
// Some messages are using legacy characters
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
public RedisBungeeVelocityListener(RedisBungeePlugin<?> plugin, List<InetAddress> exemptAddresses) {
super(plugin, exemptAddresses);
}
@Subscribe(order = PostOrder.LAST)
public void onLogin(LoginEvent event, Continuation continuation) {
plugin.executeAsync(new RedisTask<Void>(plugin) {
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
try {
if (!event.getResult().isAllowed()) {
return null;
}
if (plugin.getConfiguration().restoreOldKickBehavior()) {
for (String s : plugin.getProxiesIds()) {
if (unifiedJedis.sismember("proxy:" + s + ":usersOnline", event.getPlayer().getUniqueId().toString())) {
event.setResult(ResultedEvent.ComponentResult.denied(serializer.deserialize(plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN))));
return null;
}
}
} else if (api.isPlayerOnline(event.getPlayer().getUniqueId())) {
PlayerUtils.setKickedOtherLocation(event.getPlayer().getUniqueId().toString(), unifiedJedis);
api.kickPlayer(event.getPlayer().getUniqueId(), plugin.getConfiguration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
}
return null;
} finally {
continuation.resume();
}
}
});
}
@Override
@Subscribe
public void onPostLogin(PostLoginEvent event) {
plugin.executeAsync(new RedisTask<Void>(plugin) {
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
plugin.getUuidTranslator().persistInfo(event.getPlayer().getUsername(), event.getPlayer().getUniqueId(), unifiedJedis);
VelocityPlayerUtils.createVelocityPlayer(event.getPlayer(), unifiedJedis, true);
return null;
}
});
}
@Override
@Subscribe
public void onPlayerDisconnect(DisconnectEvent event) {
plugin.executeAsync(new RedisTask<Void>(plugin) {
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
PlayerUtils.cleanUpPlayer(event.getPlayer().getUniqueId().toString(), unifiedJedis, true);
return null;
}
});
}
@Override
@Subscribe
public void onServerChange(ServerConnectedEvent event) {
final String currentServer = event.getServer().getServerInfo().getName();
final String oldServer = event.getPreviousServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null);
plugin.executeAsync(new RedisTask<Void>(plugin) {
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
unifiedJedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", currentServer);
PayloadUtils.playerServerChangePayload(event.getPlayer().getUniqueId(), unifiedJedis, currentServer, oldServer);
return null;
}
});
}
@Override
@Subscribe(order = PostOrder.LAST) // some plugins changes it online players so we need to be executed as last
public void onPing(ProxyPingEvent event) {
if (exemptAddresses.contains(event.getConnection().getRemoteAddress().getAddress())) {
return;
}
ServerPing.Builder ping = event.getPing().asBuilder();
ping.onlinePlayers(plugin.getCount());
event.setPing(ping.build());
}
@Override
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
if (!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
return;
}
event.setResult(ForwardResult.handled());
plugin.executeAsync(() -> {
ByteArrayDataInput in = event.dataAsDataStream();
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 = original.stream()
.map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
.collect(Collectors.toSet());
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;
}
((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
});
}
@Override
@Subscribe
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);
((RedisBungeeVelocityPlugin) plugin).getProxy().getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), message);
}
}
}

View File

@@ -10,12 +10,11 @@
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;
import com.imaginarycode.minecraft.redisbungee.api.*;
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.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
@@ -23,7 +22,7 @@ import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEv
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.tasks.*;
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;
@@ -46,17 +45,21 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.scheduler.ScheduledTask;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.slf4j.Logger;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Plugin(id = "redisbungee", name = "RedisBungee", version = Constants.VERSION, url = "https://github.com/ProxioDev/RedisBungee", authors = {"astei", "ProxioDev"})
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, ConfigLoader {
@@ -64,29 +67,25 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
private final Logger logger;
private final Path dataFolder;
private final AbstractRedisBungeeAPI api;
private final PubSubListener psl;
private Summoner<?> jedisSummoner;
private RedisBungeeMode redisBungeeMode;
private final UUIDTranslator uuidTranslator;
private RedisBungeeConfiguration configuration;
private final VelocityDataManager dataManager;
private final OkHttpClient httpClient;
private volatile List<String> proxiesIds;
private final AtomicInteger globalPlayerCount = new AtomicInteger();
private ScheduledTask integrityCheck;
private final ProxyDataManager proxyDataManager;
private final VelocityPlayerDataManager playerDataManager;
private ScheduledTask cleanUpTask;
private ScheduledTask heartbeatTask;
private static final Object SERVER_TO_PLAYERS_KEY = new Object();
public static final List<ChannelIdentifier> IDENTIFIERS = List.of(
MinecraftChannelIdentifier.create("legacy", "redisbungee"),
new LegacyChannelIdentifier("RedisBungee"),
// This is needed for clients before 1.13
new LegacyChannelIdentifier("legacy:redisbungee")
);
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) {
@@ -102,11 +101,21 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
}
this.api = new RedisBungeeAPI(this);
InitialUtils.checkRedisVersion(this);
// check if this proxy is recovering from a crash and start heart the beat.
InitialUtils.checkIfRecovering(this, getDataFolder());
this.proxyDataManager = new ProxyDataManager(this) {
@Override
public Set<UUID> getLocalOnlineUUIDs() {
HashSet<UUID> players = new HashSet<>();
server.getAllPlayers().forEach(player -> players.add(player.getUniqueId()));
return players;
}
@Override
protected void handlePlatformCommandExecution(String command) {
server.getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), command);
}
};
this.playerDataManager = new VelocityPlayerDataManager(this);
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);
@@ -115,31 +124,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
}
@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
public AbstractDataManager<Player, ?, ?, ?> getDataManager() {
return this.dataManager;
}
@Override
public Summoner<?> getSummoner() {
return this.jedisSummoner;
@@ -150,29 +134,21 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.api;
}
@Override
public ProxyDataManager proxyDataManager() {
return this.proxyDataManager;
}
@Override
public PlayerDataManager<Player, ?, ?, ?, ?, ?, ?> 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) {
@@ -199,16 +175,36 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
this.getLogger().info(msg);
}
@Override
public void logInfo(String format, Object... object) {
logger.info(format, object);
}
@Override
public void logWarn(String msg) {
this.getLogger().warn(msg);
}
@Override
public void logWarn(String format, Object... object) {
logger.warn(format, object);
}
@Override
public void logFatal(String msg) {
this.getLogger().error(msg);
}
@Override
public void logFatal(String format, Throwable throwable) {
logger.error(format, throwable);
}
@Override
public RedisBungeeConfiguration configuration() {
return this.configuration;
}
@Override
public Player getPlayer(UUID uuid) {
return this.getProxy().getPlayer(uuid).orElse(null);
@@ -229,6 +225,16 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.getProxy().getPlayer(player).map(Player::getUsername).orElse(null);
}
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
@Override
public boolean handlePlatformKick(UUID uuid, String message) {
Player player = getPlayer(uuid);
if (player == null) return false;
player.disconnect(serializer.deserialize(message));
return true;
}
@Override
public String getPlayerServerName(Player player) {
return player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null);
@@ -247,25 +253,16 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
@Override
public void initialize() {
logInfo("Initializing RedisBungee.....");
updateProxiesIds();
// start heartbeat task
heartbeatTask = getProxy().getScheduler().buildTask(this, new HeartbeatTask(this, this.globalPlayerCount)).repeat(HeartbeatTask.INTERVAL, HeartbeatTask.REPEAT_INTERVAL_TIME_UNIT).schedule();
// heartbeat and clean up
this.heartbeatTask = server.getScheduler().buildTask(this, this.proxyDataManager::publishHeartbeat).repeat(Duration.ofSeconds(1)).schedule();
this.cleanUpTask = server.getScheduler().buildTask(this, this.proxyDataManager::correctionTask).repeat(Duration.ofSeconds(60)).schedule();
getProxy().getEventManager().register(this, new RedisBungeeVelocityListener(this, configuration.getExemptAddresses()));
getProxy().getEventManager().register(this, dataManager);
getProxy().getScheduler().buildTask(this, psl).schedule();
IntegrityCheckTask integrityCheckTask = new IntegrityCheckTask(this) {
@Override
public void handlePlatformPlayer(String player, UnifiedJedis unifiedJedis) {
Player playerProxied = getProxy().getPlayer(UUID.fromString(player)).orElse(null);
if (playerProxied == null)
return; // We'll deal with it later.
VelocityPlayerUtils.createVelocityPlayer(playerProxied, unifiedJedis, false);
}
};
integrityCheck = getProxy().getScheduler().buildTask(this, integrityCheckTask::execute).repeat(30, TimeUnit.SECONDS).schedule();
server.getEventManager().register(this, this.playerDataManager);
server.getEventManager().register(this, new RedisBungeeListener(this));
// subscribe
server.getScheduler().buildTask(this, this.proxyDataManager).schedule();
// register plugin messages
IDENTIFIERS.forEach(getProxy().getChannelRegistrar()::register);
@@ -292,19 +289,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
public void stop() {
logInfo("Turning off redis connections.....");
// Poison the PubSub listener
if (psl != null) {
psl.poison();
}
if (integrityCheck != null) {
integrityCheck.cancel();
if (cleanUpTask != null) {
cleanUpTask.cancel();
}
if (heartbeatTask != null) {
heartbeatTask.cancel();
}
ShutdownUtils.shutdownCleanup(this);
try {
this.proxyDataManager.close();
this.jedisSummoner.close();
} catch (IOException e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -331,10 +327,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.redisBungeeMode;
}
@Override
public void updateProxiesIds() {
this.proxiesIds = this.getCurrentProxiesIds(false);
}
@Subscribe(order = PostOrder.FIRST)
public void onProxyInitializeEvent(ProxyInitializeEvent event) {

View File

@@ -1,61 +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 com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.UUID;
public class VelocityDataManager extends AbstractDataManager<Player, PostLoginEvent, DisconnectEvent, PubSubMessageEvent> {
public VelocityDataManager(RedisBungeePlugin<Player> plugin) {
super(plugin);
}
@Override
@Subscribe
public void onPostLogin(PostLoginEvent event) {
invalidate(event.getPlayer().getUniqueId());
}
@Override
@Subscribe
public void onPlayerDisconnect(DisconnectEvent event) {
invalidate(event.getPlayer().getUniqueId());
}
@Override
@Subscribe
public void onPubSubMessage(PubSubMessageEvent event) {
handlePubSubMessage(event.getChannel(), event.getMessage());
}
private final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection();
@Override
public boolean handleKick(UUID target, String message) {
Player player = plugin.getPlayer(target);
if (player == null) {
return false;
}
player.disconnect(serializer.deserialize(message));
return true;
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class VelocityPlayerDataManager extends PlayerDataManager<Player, PostLoginEvent, DisconnectEvent, PubSubMessageEvent, PlayerChangedServerNetworkEvent, PlayerLeftNetworkEvent, ServerConnectedEvent> {
public VelocityPlayerDataManager(RedisBungeePlugin<Player> plugin) {
super(plugin);
}
@Override
@Subscribe
public void onPlayerChangedServerNetworkEvent(PlayerChangedServerNetworkEvent event) {
handleNetworkPlayerServerChange(event);
}
@Override
@Subscribe
public void onNetworkPlayerQuit(PlayerLeftNetworkEvent event) {
handleNetworkPlayerQuit(event);
}
@Override
@Subscribe
public void onPubSubMessageEvent(PubSubMessageEvent event) {
handlePubSubMessageEvent(event);
}
@Override
@Subscribe
public void onServerConnectedEvent(ServerConnectedEvent event) {
final String currentServer = event.getServer().getServerInfo().getName();
final String oldServer;
if (event.getPreviousServer().isPresent()) {
oldServer = event.getPreviousServer().get().getServerInfo().getName();
} else {
oldServer = null;
}
super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
}
private static final LegacyComponentSerializer LEGACY_COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().build();
@Subscribe
public void onLoginEvent(LoginEvent event, Continuation continuation) {
// check if online
if (getLastOnline(event.getPlayer().getUniqueId()) == 0) {
if (!plugin.configuration().restoreOldKickBehavior()) {
kickPlayer(event.getPlayer().getUniqueId(), plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.LOGGED_IN_OTHER_LOCATION));
// wait 3 seconds before releasing the event
plugin.executeAsyncAfter(continuation::resume, TimeUnit.SECONDS, 3);
} else {
event.setResult(ResultedEvent.ComponentResult.denied(LEGACY_COMPONENT_SERIALIZER.deserialize(Objects.requireNonNull(plugin.configuration().getMessages().get(RedisBungeeConfiguration.MessageType.ALREADY_LOGGED_IN)))));
continuation.resume();
}
} else {
continuation.resume();
}
}
@Override
@Subscribe
public void onLoginEvent(PostLoginEvent event) {
addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getRemoteAddress().getAddress());
}
@Override
@Subscribe
public void onDisconnectEvent(DisconnectEvent event) {
if (event.getLoginStatus() == DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN || event.getLoginStatus() == DisconnectEvent.LoginStatus.PRE_SERVER_JOIN) {
removePlayer(event.getPlayer().getUniqueId());
}
}
}

View File

@@ -1,31 +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 com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import redis.clients.jedis.UnifiedJedis;
import java.util.Optional;
public class VelocityPlayerUtils {
protected static void createVelocityPlayer(Player player, UnifiedJedis unifiedJedis, boolean fireEvent) {
Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
String serverName = null;
if (optionalServerConnection.isPresent()) {
serverName = optionalServerConnection.get().getServerInfo().getName();
}
PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, player.getRemoteAddress().getAddress(), fireEvent);
}
}

View File

@@ -10,12 +10,6 @@
package com.imaginarycode.minecraft.redisbungee.commands;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
@@ -25,11 +19,16 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
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.
@@ -324,8 +323,8 @@ public class RedisBungeeCommands {
CommandSource sender = invocation.source();
String[] args = invocation.arguments();
plugin.getProxy().getScheduler().buildTask(plugin, () -> {
String proxy = args.length >= 1 ? args[0] : plugin.getConfiguration().getProxyId();
if (!plugin.getProxiesIds().contains(proxy)) {
String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sender.sendMessage(Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED));
return;
}