mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Introduced connectionByPlatformIdentifier, removed guava usage
This commit is contained in:
@@ -32,7 +32,6 @@ import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.addon.data.PacketBlocker;
|
||||
import org.geysermc.floodgate.core.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.core.config.ProxyFloodgateConfig;
|
||||
@@ -43,7 +42,6 @@ public class BungeeDataAddon implements InjectorAddon {
|
||||
@Inject private FloodgateHandshakeHandler handshakeHandler;
|
||||
@Inject private ProxyFloodgateConfig config;
|
||||
@Inject private ProxyFloodgateApi api;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("packetHandler")
|
||||
@@ -86,14 +84,6 @@ public class BungeeDataAddon implements InjectorAddon {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
Connection player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.setPendingRemove(player)) {
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.javaUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.bungee.addon.data;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
@@ -50,11 +50,11 @@ public class BungeeProxyDataHandler extends CommonDataHandler {
|
||||
|
||||
static {
|
||||
HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler");
|
||||
checkNotNull(HANDLER, "handler field cannot be null");
|
||||
requireNonNull(HANDLER, "handler field cannot be null");
|
||||
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
requireNonNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
}
|
||||
|
||||
public BungeeProxyDataHandler(
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.bungee.addon.data;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
@@ -54,14 +54,14 @@ public class BungeeServerDataHandler extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
static {
|
||||
HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler");
|
||||
checkNotNull(HANDLER, "handler field cannot be null");
|
||||
requireNonNull(HANDLER, "handler field cannot be null");
|
||||
|
||||
USER_CONNECTION = ReflectionUtils.getField(ServerConnector.class, "user");
|
||||
checkNotNull(USER_CONNECTION, "user field cannot be null");
|
||||
requireNonNull(USER_CONNECTION, "user field cannot be null");
|
||||
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(UserConnection.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
requireNonNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
}
|
||||
|
||||
private final ProxyFloodgateApi api;
|
||||
|
||||
@@ -25,15 +25,13 @@
|
||||
|
||||
package org.geysermc.floodgate.bungee.listener;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
@@ -43,9 +41,9 @@ import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.bungee.player.BungeeConnectionManager;
|
||||
import org.geysermc.floodgate.core.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.core.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.core.listener.McListener;
|
||||
@@ -57,31 +55,23 @@ import org.geysermc.floodgate.core.util.ReflectionUtils;
|
||||
@Singleton
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public final class BungeeListener implements Listener, McListener {
|
||||
private static final Field CHANNEL_WRAPPER;
|
||||
private static final Field PLAYER_NAME;
|
||||
|
||||
static {
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
|
||||
PLAYER_NAME = ReflectionUtils.getField(InitialHandler.class, "name");
|
||||
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||
requireNonNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||
}
|
||||
|
||||
@Inject private ProxyFloodgateConfig config;
|
||||
@Inject private ProxyFloodgateApi api;
|
||||
@Inject private LanguageManager languageManager;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@Inject private SkinApplier skinApplier;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
private AttributeKey<Connection> playerAttribute;
|
||||
@Inject BungeeConnectionManager connectionManager;
|
||||
@Inject ProxyFloodgateConfig config;
|
||||
@Inject ProxyFloodgateApi api;
|
||||
@Inject LanguageManager languageManager;
|
||||
@Inject FloodgateLogger logger;
|
||||
@Inject SkinApplier skinApplier;
|
||||
|
||||
@Inject
|
||||
@Named("kickMessageAttribute")
|
||||
private AttributeKey<String> kickMessageAttribute;
|
||||
AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
@@ -90,10 +80,8 @@ public final class BungeeListener implements Listener, McListener {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingConnection connection = event.getConnection();
|
||||
|
||||
ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
|
||||
Channel channel = wrapper.getHandle();
|
||||
PendingConnection pendingConnection = event.getConnection();
|
||||
var channel = connectionManager.channelFor(pendingConnection);
|
||||
|
||||
// check if the player has to be kicked
|
||||
String kickReason = channel.attr(kickMessageAttribute).get();
|
||||
@@ -103,44 +91,41 @@ public final class BungeeListener implements Listener, McListener {
|
||||
return;
|
||||
}
|
||||
|
||||
Connection player = channel.attr(playerAttribute).get();
|
||||
if (player != null) {
|
||||
connection.setOnlineMode(false);
|
||||
connection.setUniqueId(player.javaUuid());
|
||||
ReflectionUtils.setValue(connection, PLAYER_NAME, player.javaUsername());
|
||||
Connection connection = connectionManager.connectionByPlatformIdentifier(channel);
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
pendingConnection.setOnlineMode(false);
|
||||
pendingConnection.setUniqueId(connection.javaUuid());
|
||||
ReflectionUtils.setValue(pendingConnection, PLAYER_NAME, connection.javaUsername());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLogin(LoginEvent event) {
|
||||
// if there was another player with the same uuid / name online,
|
||||
// he has been disconnected by now
|
||||
UUID uniqueId = event.getConnection().getUniqueId();
|
||||
Connection player = api.connectionByUuid(uniqueId);
|
||||
if (player != null) {
|
||||
//todo we should probably move this log message earlier in the process, so that we know
|
||||
// that Floodgate has done its job
|
||||
logger.translatedInfo(
|
||||
"floodgate.ingame.login_name",
|
||||
player.javaUsername(), uniqueId
|
||||
);
|
||||
languageManager.loadLocale(player.languageCode());
|
||||
Connection connection = api.connectionByPlatformIdentifier(event.getConnection());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
languageManager.loadLocale(connection.languageCode());
|
||||
connectionManager.addAcceptedConnection(connection);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPostLogin(PostLoginEvent event) {
|
||||
// To fix the February 2 2022 Mojang authentication changes
|
||||
if (!config.sendFloodgateData()) {
|
||||
Connection player = api.connectionByUuid(event.getPlayer().getUniqueId());
|
||||
if (player != null && !player.isLinked()) {
|
||||
skinApplier.applySkin(player, new SkinDataImpl("", ""));
|
||||
Connection connection = api.connectionByPlatformIdentifier(event.getPlayer());
|
||||
if (connection != null && !connection.isLinked()) {
|
||||
skinApplier.applySkin(connection, new SkinDataImpl("", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||
connectionManager.removeConnection(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.bungee.player;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.core.player.ConnectionManager;
|
||||
import org.geysermc.floodgate.core.util.ReflectionUtils;
|
||||
|
||||
@Singleton
|
||||
public final class BungeeConnectionManager extends ConnectionManager {
|
||||
private static final Field CHANNEL_WRAPPER;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
AttributeKey<Connection> playerAttribute;
|
||||
|
||||
@Override
|
||||
protected @Nullable Object platformIdentifierOrConnectionFor(Object input) {
|
||||
if (input instanceof ProxiedPlayer player) {
|
||||
return connectionByPlatformIdentifier(player.getPendingConnection());
|
||||
}
|
||||
if (input instanceof PendingConnection pendingConnection) {
|
||||
return channelFor(pendingConnection);
|
||||
}
|
||||
if (input instanceof Channel channel) {
|
||||
return channel.attr(playerAttribute).get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Channel channelFor(PendingConnection connection) {
|
||||
ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
|
||||
return wrapper.getHandle();
|
||||
}
|
||||
|
||||
static {
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class);
|
||||
requireNonNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.bungee.pluginmessage;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getFieldOfType;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
@@ -55,7 +55,7 @@ public final class BungeeSkinApplier implements SkinApplier {
|
||||
|
||||
static {
|
||||
LOGIN_RESULT_FIELD = getFieldOfType(InitialHandler.class, LoginResult.class);
|
||||
checkNotNull(LOGIN_RESULT_FIELD, "LoginResult field cannot be null");
|
||||
requireNonNull(LOGIN_RESULT_FIELD, "LoginResult field cannot be null");
|
||||
}
|
||||
|
||||
private final ProxyServer server = ProxyServer.getInstance();
|
||||
|
||||
@@ -24,6 +24,7 @@ dependencies {
|
||||
api(libs.micronaut.context)
|
||||
api(libs.micronaut.http.client)
|
||||
api(libs.micronaut.validation)
|
||||
annotationProcessor(libs.micronaut.validation.processor)
|
||||
|
||||
api(libs.micronaut.serde.jsonp)
|
||||
compileOnlyApi(libs.jsonb.annotations)
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
package org.geysermc.floodgate.core.addon.data;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.AttributeKey;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||
import org.geysermc.floodgate.core.config.FloodgateConfig;
|
||||
@@ -48,7 +48,7 @@ public abstract class CommonDataHandler extends ChannelInboundHandlerAdapter {
|
||||
protected final AttributeKey<String> kickMessageAttribute;
|
||||
protected final PacketBlocker blocker;
|
||||
|
||||
protected final Queue<Object> packetQueue = Queues.newConcurrentLinkedQueue();
|
||||
protected final Queue<Object> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
protected Object handshakePacket;
|
||||
protected ChannelHandlerContext ctx;
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
package org.geysermc.floodgate.core.addon.data;
|
||||
|
||||
import com.google.common.collect.Queues;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* In Floodgate the PacketBlocker is used to temporarily prevent packets from being decoded. A
|
||||
@@ -44,7 +44,7 @@ import java.util.Queue;
|
||||
* caused the server to switch to the login state.
|
||||
*/
|
||||
public class PacketBlocker extends ChannelInboundHandlerAdapter {
|
||||
private final Queue<Object> packetQueue = Queues.newConcurrentLinkedQueue();
|
||||
private final Queue<Object> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
private volatile boolean blockPackets;
|
||||
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
@@ -25,17 +25,12 @@
|
||||
|
||||
package org.geysermc.floodgate.core.api;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.micronaut.context.BeanProvider;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.GeyserApiBase;
|
||||
@@ -47,6 +42,7 @@ import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.core.http.xbox.XboxClient;
|
||||
import org.geysermc.floodgate.core.player.ConnectionManager;
|
||||
import org.geysermc.floodgate.core.pluginmessage.PluginMessageManager;
|
||||
import org.geysermc.floodgate.core.pluginmessage.channel.FormChannel;
|
||||
import org.geysermc.floodgate.core.pluginmessage.channel.TransferChannel;
|
||||
@@ -55,12 +51,7 @@ import org.geysermc.floodgate.core.scope.ServerOnly;
|
||||
@ServerOnly
|
||||
@Singleton
|
||||
public class SimpleFloodgateApi implements GeyserApiBase {
|
||||
private final Map<UUID, Connection> players = new ConcurrentHashMap<>();
|
||||
private final Cache<UUID, Connection> pendingRemove =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
@Inject ConnectionManager connectionManager;
|
||||
@Inject BeanProvider<PluginMessageManager> pluginMessageManager;
|
||||
@Inject FloodgateConfig config;
|
||||
@Inject FloodgateLogger logger;
|
||||
@@ -73,12 +64,12 @@ public class SimpleFloodgateApi implements GeyserApiBase {
|
||||
|
||||
@Override
|
||||
public @NonNull List<? extends Connection> onlineConnections() {
|
||||
return ImmutableList.copyOf(players.values());
|
||||
return new ArrayList<>(connectionManager.acceptedConnections());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onlineConnectionsCount() {
|
||||
return players.size();
|
||||
return connectionManager.acceptedConnectionsCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,31 +79,16 @@ public class SimpleFloodgateApi implements GeyserApiBase {
|
||||
|
||||
@Override
|
||||
public @Nullable Connection connectionByUuid(@NonNull UUID uuid) {
|
||||
Connection selfPlayer = players.get(uuid);
|
||||
if (selfPlayer != null) {
|
||||
return selfPlayer;
|
||||
}
|
||||
return connectionManager.connectionByUuid(uuid);
|
||||
}
|
||||
|
||||
// bedrock players are always stored by their xuid,
|
||||
// so we return the instance if we know that the given uuid is a Floodgate uuid
|
||||
if (isFloodgateId(uuid)) {
|
||||
return pendingRemove.getIfPresent(uuid);
|
||||
}
|
||||
|
||||
// make it possible to find player by Java id (linked players)
|
||||
// TODO still needed?
|
||||
for (Connection player : players.values()) {
|
||||
if (player.javaUuid().equals(uuid)) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
// and don't forget the pending remove linked players
|
||||
return getPendingRemovePlayer(uuid);
|
||||
public @Nullable Connection connectionByPlatformIdentifier(@NonNull Object platformIdentifier) {
|
||||
return connectionManager.connectionByPlatformIdentifier(platformIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Connection connectionByXuid(@NonNull String s) {
|
||||
return null;
|
||||
public @Nullable Connection connectionByXuid(@NonNull String xuid) {
|
||||
return connectionManager.connectionByXuid(xuid);
|
||||
}
|
||||
|
||||
public boolean isFloodgateId(UUID uuid) {
|
||||
@@ -157,43 +133,6 @@ public class SimpleFloodgateApi implements GeyserApiBase {
|
||||
}
|
||||
*/
|
||||
|
||||
public Connection addPlayer(Connection player) {
|
||||
// Bedrock players are always stored by their xuid
|
||||
return players.put(player.javaUuid(), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked when the player is no longer on the server, but the related platform-
|
||||
* dependant event hasn't fired yet
|
||||
* @param player
|
||||
*/
|
||||
public boolean setPendingRemove(Connection player) {
|
||||
pendingRemove.put(player.javaUuid(), player);
|
||||
return players.remove(player.javaUuid(), player);
|
||||
}
|
||||
|
||||
public void playerRemoved(UUID correctUuid) {
|
||||
// we can remove the player directly if it is a Floodgate UUID.
|
||||
// since it's stored by their Floodgate UUID
|
||||
if (isFloodgateId(correctUuid)) {
|
||||
pendingRemove.invalidate(correctUuid);
|
||||
return;
|
||||
}
|
||||
Connection linkedPlayer = getPendingRemovePlayer(correctUuid);
|
||||
if (linkedPlayer != null) {
|
||||
pendingRemove.invalidate(linkedPlayer.javaUuid());
|
||||
}
|
||||
}
|
||||
|
||||
private Connection getPendingRemovePlayer(UUID correctUuid) {
|
||||
for (Connection player : pendingRemove.asMap().values()) {
|
||||
if (player.javaUuid().equals(correctUuid)) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PlayerLink getPlayerLink() { // TODO
|
||||
return InstanceHolder.getPlayerLink();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.micronaut.core.annotation.AnnotationMetadata;
|
||||
import io.micronaut.inject.qualifiers.Qualifiers;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -40,15 +41,13 @@ import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.event.FloodgateSubscriber;
|
||||
import org.geysermc.floodgate.core.util.EagerSingleton;
|
||||
|
||||
@EagerSingleton
|
||||
@Singleton
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EventBus extends EventBusImpl<Object, FloodgateSubscriber<?>>
|
||||
public class EventBus extends EventBusImpl<Object, FloodgateSubscriber<?>>
|
||||
implements FloodgateEventBus {
|
||||
@Inject ApplicationContext context;
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean fire(@NonNull Object event) {
|
||||
|
||||
@@ -82,10 +82,10 @@ public abstract class CommandUtil {
|
||||
|
||||
protected abstract Collection<?> getOnlinePlayers();
|
||||
|
||||
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
||||
public @NonNull List<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
||||
Collection<?> players = getOnlinePlayers();
|
||||
|
||||
Collection<String> usernames = new ArrayList<>();
|
||||
List<String> usernames = new ArrayList<>();
|
||||
switch (limitTo) {
|
||||
case ALL_PLAYERS:
|
||||
for (Object player : players) {
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.core.player;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.WeakHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
|
||||
public abstract class ConnectionManager {
|
||||
private final Set<Connection> connections = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<Connection> pendingConnections = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private final Map<String, Connection> xuidToConnection =
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<UUID, Connection> uuidToConnection =
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
protected final Map<Object, Connection> platformIdentifierToConnection =
|
||||
Collections.synchronizedMap(new WeakHashMap<>());
|
||||
|
||||
@Inject FloodgateLogger logger;
|
||||
|
||||
public Connection connectionByUuid(UUID javaId) {
|
||||
return uuidToConnection.get(javaId);
|
||||
}
|
||||
|
||||
public Connection connectionByXuid(String xuid) {
|
||||
return xuidToConnection.get(xuid);
|
||||
}
|
||||
|
||||
public @Nullable Connection connectionByPlatformIdentifier(@NonNull Object platformIdentifier) {
|
||||
Objects.requireNonNull(platformIdentifier);
|
||||
var connection = platformIdentifierToConnection.get(platformIdentifier);
|
||||
if (connection != null) {
|
||||
return connection;
|
||||
}
|
||||
// try to fetch a connection or return a different platform identifier we can try
|
||||
var identifierOrConnection = platformIdentifierOrConnectionFor(platformIdentifier);
|
||||
if (identifierOrConnection == null) {
|
||||
return null;
|
||||
}
|
||||
// if it returns a connection it found a way to fetch it from the given platformIdentifier
|
||||
if (identifierOrConnection instanceof Connection foundConnection) {
|
||||
platformIdentifierToConnection.put(platformIdentifier, foundConnection);
|
||||
return foundConnection;
|
||||
}
|
||||
// if it returns a different platform identifier,
|
||||
// call this method again with the new identifier and store the result if it returned any
|
||||
connection = connectionByPlatformIdentifier(identifierOrConnection);
|
||||
if (connection != null) {
|
||||
platformIdentifierToConnection.put(identifierOrConnection, connection);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected abstract @Nullable Object platformIdentifierOrConnectionFor(Object input);
|
||||
|
||||
public void addConnection(Connection connection) {
|
||||
connections.add(connection);
|
||||
pendingConnections.add(connection);
|
||||
|
||||
logger.translatedInfo(
|
||||
"floodgate.ingame.login_name",
|
||||
connection.javaUsername(), connection.javaUuid()
|
||||
);
|
||||
}
|
||||
|
||||
public boolean addAcceptedConnection(Connection connection) {
|
||||
pendingConnections.remove(connection);
|
||||
|
||||
xuidToConnection.put(connection.xuid(), connection);
|
||||
var old = uuidToConnection.put(connection.javaUuid(), connection);
|
||||
if (old == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug(String.format(
|
||||
"Replaced Floodgate player playing as %s uuid %s with %s uuid %s",
|
||||
old.javaUsername(), old.javaUuid(),
|
||||
connection.javaUsername(), connection.javaUuid()
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
public Connection findPendingConnection(UUID javaId) {
|
||||
for (Connection pendingConnection : pendingConnections) {
|
||||
if (pendingConnection.javaUuid().equals(javaId)) {
|
||||
return pendingConnection;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeConnection(Object platformIdentifier) {
|
||||
var connection = connectionByPlatformIdentifier(platformIdentifier);
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
connections.remove(connection);
|
||||
pendingConnections.remove(connection);
|
||||
uuidToConnection.remove(connection.javaUuid(), connection);
|
||||
xuidToConnection.remove(connection.xuid(), connection);
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", connection.javaUsername());
|
||||
}
|
||||
|
||||
public Collection<Connection> acceptedConnections() {
|
||||
return uuidToConnection.values();
|
||||
}
|
||||
|
||||
public int acceptedConnectionsCount() {
|
||||
return uuidToConnection.size();
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public final class FloodgateConnection implements Connection {
|
||||
private final PropertyGlue propertyGlue = new PropertyGlue();
|
||||
private LegacyPlayerWrapper legacyPlayer;
|
||||
|
||||
static FloodgateConnection from(BedrockData data, HandshakeData handshakeData, int port) {
|
||||
static FloodgateConnection from(BedrockData data, HandshakeData handshakeData) {
|
||||
UUID javaUniqueId = Utils.getJavaUuid(data.getXuid());
|
||||
|
||||
BedrockPlatform deviceOs = BedrockPlatform.fromId(data.getDeviceOs());
|
||||
@@ -78,7 +78,7 @@ public final class FloodgateConnection implements Connection {
|
||||
|
||||
LinkedPlayer linkedPlayer = handshakeData.getLinkedPlayer();
|
||||
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(data.getIp(), port);
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(data.getIp(), 0);
|
||||
|
||||
return new FloodgateConnection(
|
||||
data.getVersion(), data.getUsername(), handshakeData.getJavaUsername(),
|
||||
|
||||
@@ -29,7 +29,6 @@ import static org.geysermc.floodgate.core.player.FloodgateHandshakeHandler.Resul
|
||||
import static org.geysermc.floodgate.core.player.FloodgateHandshakeHandler.ResultType.NOT_FLOODGATE_DATA;
|
||||
import static org.geysermc.floodgate.util.BedrockData.EXPECTED_LENGTH;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
@@ -37,6 +36,7 @@ import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import lombok.AccessLevel;
|
||||
@@ -61,6 +61,7 @@ import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
public final class FloodgateHandshakeHandler {
|
||||
@Inject ConnectionManager connectionManager;
|
||||
@Inject HandshakeHandlersImpl handshakeHandlers;
|
||||
@Inject SimpleFloodgateApi api;
|
||||
@Inject CommonPlayerLink link;
|
||||
@@ -106,9 +107,9 @@ public final class FloodgateHandshakeHandler {
|
||||
public CompletableFuture<HandshakeResult> handle(
|
||||
@NonNull Channel channel,
|
||||
@NonNull String floodgateDataString,
|
||||
@NonNull String hostname) {
|
||||
|
||||
byte[] floodgateData = floodgateDataString.getBytes(Charsets.UTF_8);
|
||||
@NonNull String hostname
|
||||
) {
|
||||
byte[] floodgateData = floodgateDataString.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
|
||||
@@ -193,8 +194,8 @@ public final class FloodgateHandshakeHandler {
|
||||
Channel channel,
|
||||
String hostname,
|
||||
BedrockData bedrockData,
|
||||
LinkedPlayer linkedPlayer) {
|
||||
|
||||
LinkedPlayer linkedPlayer
|
||||
) {
|
||||
try {
|
||||
HandshakeData handshakeData = new HandshakeDataImpl(
|
||||
channel, true, bedrockData.clone(), config,
|
||||
@@ -216,15 +217,12 @@ public final class FloodgateHandshakeHandler {
|
||||
bedrockData.getVerifyCode());
|
||||
}
|
||||
|
||||
int port = ((InetSocketAddress) channel.remoteAddress()).getPort();
|
||||
var connection = FloodgateConnection.from(bedrockData, handshakeData);
|
||||
|
||||
Connection player = FloodgateConnection.from(bedrockData, handshakeData, port);
|
||||
connectionManager.addConnection(connection);
|
||||
channel.attr(playerAttribute).set(connection);
|
||||
|
||||
api.addPlayer(player);
|
||||
|
||||
channel.attr(playerAttribute).set(player);
|
||||
|
||||
return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player);
|
||||
return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, connection);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
return callHandlerAndReturnResult(ResultType.EXCEPTION, channel, null, hostname);
|
||||
@@ -235,8 +233,8 @@ public final class FloodgateHandshakeHandler {
|
||||
ResultType resultType,
|
||||
Channel channel,
|
||||
BedrockData bedrockData,
|
||||
String hostname) {
|
||||
|
||||
String hostname
|
||||
) {
|
||||
HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null,
|
||||
bedrockData, config, null, hostname);
|
||||
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
||||
|
||||
@@ -29,7 +29,8 @@ import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
@@ -170,19 +171,19 @@ public class ProfileAudienceArgument extends CommandArgument<UserAudience, Profi
|
||||
String trimmedInput = input.trim();
|
||||
|
||||
if (trimmedInput.isEmpty()) {
|
||||
return ImmutableList.copyOf(commandUtil.getOnlineUsernames(limitTo));
|
||||
return Collections.unmodifiableList(commandUtil.getOnlineUsernames(limitTo));
|
||||
}
|
||||
|
||||
String lowercaseInput = input.toLowerCase(Locale.ROOT);
|
||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||
List<String> profileSuggestions = new ArrayList<>();
|
||||
|
||||
for (final String player : commandUtil.getOnlineUsernames(limitTo)) {
|
||||
if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) {
|
||||
builder.add(player);
|
||||
profileSuggestions.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
return Collections.unmodifiableList(profileSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
|
||||
package org.geysermc.floodgate.core.pluginmessage.channel;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
|
||||
import jakarta.inject.Inject;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
@@ -110,7 +110,7 @@ public class FormChannel implements PluginMessageChannel {
|
||||
byte[] jsonData =
|
||||
definition.codec()
|
||||
.jsonData(form)
|
||||
.getBytes(Charsets.UTF_8);
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] data = new byte[jsonData.length + 3];
|
||||
data[0] = (byte) definition.formType().ordinal();
|
||||
@@ -123,7 +123,7 @@ public class FormChannel implements PluginMessageChannel {
|
||||
protected boolean callResponseConsumer(byte[] data) {
|
||||
Form storedForm = storedForms.remove(getFormId(data));
|
||||
if (storedForm != null) {
|
||||
String responseData = new String(data, 2, data.length - 2, Charsets.UTF_8);
|
||||
String responseData = new String(data, 2, data.length - 2, StandardCharsets.UTF_8);
|
||||
try {
|
||||
formDefinitions.definitionFor(storedForm)
|
||||
.handleFormResponse(storedForm, responseData);
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.geysermc.api.GeyserApiBase;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.util.Utils;
|
||||
import org.geysermc.floodgate.core.util.WebsocketEventType;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
@@ -107,7 +106,7 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
break;
|
||||
case SKIN_UPLOADED:
|
||||
String xuid = message.get("xuid").getAsString();
|
||||
Connection player = api.connectionByUuid(Utils.getJavaUuid(xuid));
|
||||
Connection player = api.connectionByXuid(xuid);
|
||||
if (player != null) {
|
||||
if (!message.get("success").getAsBoolean()) {
|
||||
logger.info("Failed to upload skin for {} ({})", xuid,
|
||||
|
||||
@@ -25,15 +25,16 @@
|
||||
|
||||
package org.geysermc.floodgate.core.util;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.net.URL;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.config.FloodgateConfig;
|
||||
@@ -186,7 +187,8 @@ public final class LanguageManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
private String formatNotFound(String key, Object... args) {
|
||||
return key + " " + Joiner.on(", ").join(args);
|
||||
private String formatNotFound(String key, Object... rawArgs) {
|
||||
var args = Arrays.stream(rawArgs).map(Object::toString).collect(Collectors.joining(", "));
|
||||
return key + " " + args;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@ java-websocket = "1.5.2"
|
||||
cloud = "1.5.0"
|
||||
snakeyaml = "2.0"
|
||||
bstats = "3.0.2"
|
||||
# todo remove guava from events to be able to remove it here
|
||||
guava = "31.1-jre"
|
||||
|
||||
# bungee
|
||||
bungee = "dfd847f"
|
||||
bungee = "master-SNAPSHOT"
|
||||
|
||||
# spigot
|
||||
folia = "1.19.4-R0.1-SNAPSHOT"
|
||||
@@ -61,6 +62,7 @@ micronaut-inject = { module = "io.micronaut:micronaut-inject-java" }
|
||||
micronaut-context = { module = "io.micronaut:micronaut-context" }
|
||||
micronaut-http-client = { module = "io.micronaut:micronaut-http-client-jdk" }
|
||||
micronaut-validation = { module = "io.micronaut.validation:micronaut-validation" }
|
||||
micronaut-validation-processor = { module = "io.micronaut.validation:micronaut-validation-processor" }
|
||||
micronaut-data-processor = { module = "io.micronaut.data:micronaut-data-processor" }
|
||||
micronaut-hibernate = { module = "io.micronaut.data:micronaut-data-hibernate-jpa" }
|
||||
micronaut-hikari = { module = "io.micronaut.sql:micronaut-jdbc-hikari" }
|
||||
|
||||
@@ -30,10 +30,7 @@ import io.netty.util.AttributeKey;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.core.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.core.player.FloodgateHandshakeHandler;
|
||||
|
||||
@@ -41,8 +38,6 @@ import org.geysermc.floodgate.core.player.FloodgateHandshakeHandler;
|
||||
public final class SpigotDataAddon implements InjectorAddon {
|
||||
@Inject FloodgateHandshakeHandler handshakeHandler;
|
||||
@Inject FloodgateConfig config;
|
||||
@Inject SimpleFloodgateApi api;
|
||||
@Inject FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("packetHandler")
|
||||
@@ -52,10 +47,6 @@ public final class SpigotDataAddon implements InjectorAddon {
|
||||
@Named("kickMessageAttribute")
|
||||
AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
AttributeKey<Connection> playerAttribute;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean toServer) {
|
||||
// we have to add the packet blocker in the data handler, otherwise ProtocolSupport breaks
|
||||
@@ -65,14 +56,6 @@ public final class SpigotDataAddon implements InjectorAddon {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
Connection player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.setPendingRemove(player)) {
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.javaUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
}
|
||||
|
||||
@@ -27,47 +27,39 @@ package org.geysermc.floodgate.spigot.listener;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.core.listener.McListener;
|
||||
import org.geysermc.floodgate.core.player.ConnectionManager;
|
||||
import org.geysermc.floodgate.core.util.LanguageManager;
|
||||
|
||||
@Singleton
|
||||
public final class SpigotListener implements Listener, McListener {
|
||||
@Inject ConnectionManager connectionManager;
|
||||
@Inject SimpleFloodgateApi api;
|
||||
@Inject LanguageManager languageManager;
|
||||
@Inject FloodgateLogger logger;
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
UUID uniqueId = event.getPlayer().getUniqueId();
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
// see SpigotConnectionManager#platformIdentifierOrConnectionFor for more info
|
||||
|
||||
var connection = connectionManager.findPendingConnection(event.getPlayer().getUniqueId());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there was another player with the same uuid online,
|
||||
// he would've been disconnected by now
|
||||
Connection player = api.connectionByUuid(uniqueId);
|
||||
if (player != null) {
|
||||
//todo we should probably move this log message earlier in the process, so that we know
|
||||
// that Floodgate has done its job
|
||||
logger.translatedInfo(
|
||||
"floodgate.ingame.login_name",
|
||||
player.javaUsername(), player.javaUuid()
|
||||
);
|
||||
languageManager.loadLocale(player.languageCode());
|
||||
}
|
||||
languageManager.loadLocale(connection.languageCode());
|
||||
connectionManager.addAcceptedConnection(connection);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||
connectionManager.removeConnection(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.spigot.player;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.floodgate.core.player.ConnectionManager;
|
||||
|
||||
@Singleton
|
||||
public class SpigotConnectionManager extends ConnectionManager {
|
||||
@Override
|
||||
protected @Nullable Object platformIdentifierOrConnectionFor(Object input) {
|
||||
// in PlayerList#canPlayerLogin the old players with the same profile are disconnected (
|
||||
// PlayerKickEvent, see ServerGamePacketListener#disconnect &
|
||||
// PlayerQuitEvent, see PlayerList#remove
|
||||
// ) before the first event runs with the Player instance (PlayerLoginEvent).
|
||||
// This means that we can always use the Player's uuid
|
||||
if (input instanceof Player player) {
|
||||
return connectionByUuid(player.getUniqueId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.core.event.EventBus;
|
||||
import org.geysermc.floodgate.core.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.core.skin.SkinApplier;
|
||||
@@ -75,8 +74,6 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
throw new IllegalStateException("The GameProfile cannot be null! " + player.getName());
|
||||
}
|
||||
|
||||
// Need to be careful here - getProperties() returns an authlib PropertyMap, which extends
|
||||
// MultiMap from Guava. Floodgate relocates Guava.
|
||||
PropertyMap properties = profile.getProperties();
|
||||
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
@@ -35,13 +35,13 @@ import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@@ -90,7 +90,7 @@ public class ClassNames {
|
||||
Class<?> craftPlayerClass = ReflectionUtils.getClass(
|
||||
"org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
|
||||
GET_PROFILE_METHOD = getMethod(craftPlayerClass, "getProfile");
|
||||
checkNotNull(GET_PROFILE_METHOD, "Get profile method");
|
||||
requireNonNull(GET_PROFILE_METHOD, "Get profile method");
|
||||
|
||||
String nmsPackage = SPIGOT_MAPPING_PREFIX + '.';
|
||||
|
||||
@@ -129,7 +129,7 @@ public class ClassNames {
|
||||
);
|
||||
|
||||
HANDSHAKE_HOST = getFieldOfType(HANDSHAKE_PACKET, String.class);
|
||||
checkNotNull(HANDSHAKE_HOST, "Handshake host");
|
||||
requireNonNull(HANDSHAKE_HOST, "Handshake host");
|
||||
|
||||
LOGIN_START_PACKET = getClassOrFallback(
|
||||
"net.minecraft.network.protocol.login.PacketLoginInStart",
|
||||
@@ -142,10 +142,10 @@ public class ClassNames {
|
||||
);
|
||||
|
||||
LOGIN_PROFILE = getFieldOfType(LOGIN_LISTENER, GameProfile.class);
|
||||
checkNotNull(LOGIN_PROFILE, "Profile from LoginListener");
|
||||
requireNonNull(LOGIN_PROFILE, "Profile from LoginListener");
|
||||
|
||||
LOGIN_DISCONNECT = getMethod(LOGIN_LISTENER, "disconnect", String.class);
|
||||
checkNotNull(LOGIN_DISCONNECT, "LoginListener's disconnect method");
|
||||
requireNonNull(LOGIN_DISCONNECT, "LoginListener's disconnect method");
|
||||
|
||||
NETWORK_EXCEPTION_CAUGHT = getMethod(
|
||||
networkManager,
|
||||
@@ -155,14 +155,14 @@ public class ClassNames {
|
||||
|
||||
// there are multiple no-arg void methods
|
||||
INIT_UUID = getMethod(LOGIN_LISTENER, "initUUID");
|
||||
checkNotNull(INIT_UUID, "initUUID from LoginListener");
|
||||
requireNonNull(INIT_UUID, "initUUID from LoginListener");
|
||||
|
||||
Class<?> packetListenerClass = getClassOrFallback(
|
||||
"net.minecraft.network.PacketListener",
|
||||
nmsPackage + "PacketListener"
|
||||
);
|
||||
PACKET_LISTENER = getFieldOfType(networkManager, packetListenerClass);
|
||||
checkNotNull(PACKET_LISTENER, "Packet listener");
|
||||
requireNonNull(PACKET_LISTENER, "Packet listener");
|
||||
|
||||
LOGIN_HANDLER = getClassOrFallback(
|
||||
"net.minecraft.server.network.LoginListener$LoginHandler",
|
||||
@@ -171,10 +171,10 @@ public class ClassNames {
|
||||
|
||||
LOGIN_HANDLER_CONSTRUCTOR =
|
||||
ReflectionUtils.getConstructor(LOGIN_HANDLER, true, LOGIN_LISTENER);
|
||||
checkNotNull(LOGIN_HANDLER_CONSTRUCTOR, "LoginHandler constructor");
|
||||
requireNonNull(LOGIN_HANDLER_CONSTRUCTOR, "LoginHandler constructor");
|
||||
|
||||
FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents");
|
||||
checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
||||
requireNonNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
||||
|
||||
|
||||
PAPER_DISABLE_USERNAME_VALIDATION = getField(LOGIN_LISTENER,
|
||||
@@ -187,23 +187,23 @@ public class ClassNames {
|
||||
|
||||
// ProxyUtils
|
||||
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
||||
checkNotNull(spigotConfig, "Spigot config");
|
||||
requireNonNull(spigotConfig, "Spigot config");
|
||||
|
||||
BUNGEE = getField(spigotConfig, "bungee");
|
||||
checkNotNull(BUNGEE, "Bungee field");
|
||||
requireNonNull(BUNGEE, "Bungee field");
|
||||
|
||||
Class<?> paperConfigNew = getClassSilently(
|
||||
"io.papermc.paper.configuration.GlobalConfiguration");
|
||||
if (paperConfigNew != null) {
|
||||
// 1.19 and later
|
||||
Method paperConfigGet = checkNotNull(getMethod(paperConfigNew, "get"),
|
||||
Method paperConfigGet = requireNonNull(getMethod(paperConfigNew, "get"),
|
||||
"GlobalConfiguration get");
|
||||
Field paperConfigProxies = checkNotNull(getField(paperConfigNew, "proxies"),
|
||||
Field paperConfigProxies = requireNonNull(getField(paperConfigNew, "proxies"),
|
||||
"Proxies field");
|
||||
Field paperConfigVelocity = checkNotNull(
|
||||
Field paperConfigVelocity = requireNonNull(
|
||||
getField(paperConfigProxies.getType(), "velocity"),
|
||||
"velocity field");
|
||||
Field paperVelocityEnabled = checkNotNull(
|
||||
Field paperVelocityEnabled = requireNonNull(
|
||||
getField(paperConfigVelocity.getType(), "enabled"),
|
||||
"Velocity enabled field");
|
||||
PAPER_VELOCITY_SUPPORT = () -> {
|
||||
@@ -232,7 +232,7 @@ public class ClassNames {
|
||||
) != null;
|
||||
}
|
||||
|
||||
private static <T> T checkNotNull(T toCheck, String objectName) {
|
||||
return Preconditions.checkNotNull(toCheck, objectName + " cannot be null");
|
||||
private static <T> T requireNonNull(T toCheck, String objectName) {
|
||||
return Objects.requireNonNull(toCheck, objectName + " cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
var log4jVersion = "2.11.2"
|
||||
var gsonVersion = "2.8.8"
|
||||
var guavaVersion = "25.1-jre"
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
|
||||
@@ -89,14 +89,6 @@ public final class VelocityDataAddon implements InjectorAddon {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
Connection player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.setPendingRemove(player)) {
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.javaUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.velocity.addon.data;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethodByName;
|
||||
@@ -38,7 +38,6 @@ import io.netty.util.AttributeKey;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.core.addon.data.CommonDataHandler;
|
||||
import org.geysermc.floodgate.core.addon.data.PacketBlocker;
|
||||
@@ -60,30 +59,30 @@ public final class VelocityProxyDataHandler extends CommonDataHandler {
|
||||
|
||||
static {
|
||||
Class<?> iic = getPrefixedClass("connection.client.InitialInboundConnection");
|
||||
checkNotNull(iic, "InitialInboundConnection class cannot be null");
|
||||
requireNonNull(iic, "InitialInboundConnection class cannot be null");
|
||||
|
||||
HANDSHAKE = getField(iic, "handshake");
|
||||
checkNotNull(HANDSHAKE, "Handshake field cannot be null");
|
||||
requireNonNull(HANDSHAKE, "Handshake field cannot be null");
|
||||
|
||||
HANDSHAKE_PACKET = getPrefixedClass("protocol.packet.Handshake");
|
||||
checkNotNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
|
||||
requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
|
||||
|
||||
HANDSHAKE_SERVER_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");
|
||||
checkNotNull(HANDSHAKE_SERVER_ADDRESS, "Address in the Handshake packet cannot be null");
|
||||
requireNonNull(HANDSHAKE_SERVER_ADDRESS, "Address in the Handshake packet cannot be null");
|
||||
|
||||
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
|
||||
REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress");
|
||||
checkNotNull(REMOTE_ADDRESS, "remoteAddress cannot be null");
|
||||
requireNonNull(REMOTE_ADDRESS, "remoteAddress cannot be null");
|
||||
|
||||
SERVER_LOGIN_PACKET = getPrefixedClass("protocol.packet.ServerLogin");
|
||||
checkNotNull(SERVER_LOGIN_PACKET, "ServerLogin packet class cannot be null");
|
||||
requireNonNull(SERVER_LOGIN_PACKET, "ServerLogin packet class cannot be null");
|
||||
|
||||
GET_SESSION_HANDLER = getMethodByName(minecraftConnection, "getSessionHandler", true);
|
||||
checkNotNull(GET_SESSION_HANDLER, "getSessionHandler method cannot be null");
|
||||
requireNonNull(GET_SESSION_HANDLER, "getSessionHandler method cannot be null");
|
||||
|
||||
INITIAL_LOGIN_SESSION_HANDLER =
|
||||
getPrefixedClass("connection.client.InitialLoginSessionHandler");
|
||||
checkNotNull(INITIAL_LOGIN_SESSION_HANDLER, "InitialLoginSessionHandler cannot be null");
|
||||
requireNonNull(INITIAL_LOGIN_SESSION_HANDLER, "InitialLoginSessionHandler cannot be null");
|
||||
|
||||
// allowed to be null if it's an old Velocity version
|
||||
FORCE_KEY_AUTHENTICATION = getField(INITIAL_LOGIN_SESSION_HANDLER, "forceKeyAuthentication");
|
||||
@@ -115,14 +114,9 @@ public final class VelocityProxyDataHandler extends CommonDataHandler {
|
||||
@Override
|
||||
protected boolean shouldRemoveHandler(HandshakeResult result) {
|
||||
if (result.getResultType() == ResultType.SUCCESS) {
|
||||
Connection player = result.getFloodgatePlayer();
|
||||
logger.info("Floodgate player who is logged in as {} {} joined",
|
||||
player.javaUsername(), player.javaUuid());
|
||||
|
||||
// the way Velocity stores whether to force key authentication
|
||||
boolean forceKeyAuthentication = Boolean.getBoolean("auth.forceSecureProfiles");
|
||||
// we need the login packet to bypass the 'force key authentication'
|
||||
return !forceKeyAuthentication;
|
||||
return !Boolean.getBoolean("auth.forceSecureProfiles");
|
||||
}
|
||||
return super.shouldRemoveHandler(result);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.velocity.addon.data;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.castedInvoke;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
|
||||
@@ -54,20 +54,20 @@ public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapt
|
||||
|
||||
static {
|
||||
HANDSHAKE_PACKET = getPrefixedClass("protocol.packet.Handshake");
|
||||
checkNotNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
|
||||
requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null");
|
||||
|
||||
HANDSHAKE_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");
|
||||
checkNotNull(HANDSHAKE_ADDRESS, "Address field of the Handshake packet cannot be null");
|
||||
requireNonNull(HANDSHAKE_ADDRESS, "Address field of the Handshake packet cannot be null");
|
||||
|
||||
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
|
||||
|
||||
GET_ASSOCIATION = getMethod(minecraftConnection, "getAssociation");
|
||||
checkNotNull(GET_ASSOCIATION, "getAssociation in MinecraftConnection cannot be null");
|
||||
requireNonNull(GET_ASSOCIATION, "getAssociation in MinecraftConnection cannot be null");
|
||||
|
||||
Class<?> serverConnection = getPrefixedClass("connection.backend.VelocityServerConnection");
|
||||
|
||||
GET_PLAYER = getMethod(serverConnection, "getPlayer");
|
||||
checkNotNull(GET_PLAYER, "getPlayer in VelocityServerConnection cannot be null");
|
||||
requireNonNull(GET_PLAYER, "getPlayer in VelocityServerConnection cannot be null");
|
||||
}
|
||||
|
||||
private final ProxyFloodgateApi api;
|
||||
|
||||
@@ -25,22 +25,12 @@
|
||||
|
||||
package org.geysermc.floodgate.velocity.listener;
|
||||
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getFieldOfType;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClassSilently;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
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.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.api.util.GameProfile.Property;
|
||||
import io.netty.channel.Channel;
|
||||
@@ -48,10 +38,7 @@ import io.netty.util.AttributeKey;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
@@ -59,40 +46,11 @@ import org.geysermc.floodgate.core.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.core.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.core.listener.McListener;
|
||||
import org.geysermc.floodgate.core.util.LanguageManager;
|
||||
import org.geysermc.floodgate.velocity.player.VelocityConnectionManager;
|
||||
|
||||
@Singleton
|
||||
public final class VelocityListener implements McListener {
|
||||
private static final Field INITIAL_MINECRAFT_CONNECTION;
|
||||
private static final Field INITIAL_CONNECTION_DELEGATE;
|
||||
private static final Field CHANNEL;
|
||||
|
||||
static {
|
||||
Class<?> initialConnection = getPrefixedClass("connection.client.InitialInboundConnection");
|
||||
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
|
||||
INITIAL_MINECRAFT_CONNECTION = getFieldOfType(initialConnection, minecraftConnection);
|
||||
|
||||
// Since Velocity 3.1.0
|
||||
Class<?> loginInboundConnection =
|
||||
getPrefixedClassSilently("connection.client.LoginInboundConnection");
|
||||
if (loginInboundConnection != null) {
|
||||
INITIAL_CONNECTION_DELEGATE = getField(loginInboundConnection, "delegate");
|
||||
Objects.requireNonNull(
|
||||
INITIAL_CONNECTION_DELEGATE,
|
||||
"initial inbound connection delegate cannot be null"
|
||||
);
|
||||
} else {
|
||||
INITIAL_CONNECTION_DELEGATE = null;
|
||||
}
|
||||
|
||||
CHANNEL = getFieldOfType(minecraftConnection, Channel.class);
|
||||
}
|
||||
|
||||
private final Cache<InboundConnection, Connection> playerCache =
|
||||
CacheBuilder.newBuilder()
|
||||
.maximumSize(500)
|
||||
.expireAfterAccess(20, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
@Inject VelocityConnectionManager connectionManager;
|
||||
@Inject ProxyFloodgateConfig config;
|
||||
@Inject ProxyFloodgateApi api;
|
||||
@Inject LanguageManager languageManager;
|
||||
@@ -106,26 +64,17 @@ public final class VelocityListener implements McListener {
|
||||
@Named("kickMessageAttribute")
|
||||
AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@Subscribe(order = PostOrder.EARLY)
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
Connection player = null;
|
||||
String kickMessage;
|
||||
try {
|
||||
InboundConnection connection = event.getConnection();
|
||||
if (INITIAL_CONNECTION_DELEGATE != null) {
|
||||
// Velocity 3.1.0 added LoginInboundConnection which is used in the login state,
|
||||
// but that class doesn't have a Channel field. However, it does have
|
||||
// InitialInboundConnection as a field
|
||||
connection = getCastedValue(connection, INITIAL_CONNECTION_DELEGATE);
|
||||
}
|
||||
Object mcConnection = getValue(connection, INITIAL_MINECRAFT_CONNECTION);
|
||||
Channel channel = getCastedValue(mcConnection, CHANNEL);
|
||||
|
||||
Channel channel = connectionManager.channelFor(event.getConnection());
|
||||
player = channel.attr(playerAttribute).get();
|
||||
kickMessage = channel.attr(kickMessageAttribute).get();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Failed get the FloodgatePlayer from the player's channel", exception);
|
||||
kickMessage = "Failed to get the FloodgatePlayer from the players's Channel";
|
||||
kickMessage = "Failed to get the FloodgatePlayer from the player's Channel";
|
||||
}
|
||||
|
||||
if (kickMessage != null) {
|
||||
@@ -137,41 +86,44 @@ public final class VelocityListener implements McListener {
|
||||
|
||||
if (player != null) {
|
||||
event.setResult(PreLoginEvent.PreLoginComponentResult.forceOfflineMode());
|
||||
playerCache.put(event.getConnection(), player);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.EARLY)
|
||||
public void onGameProfileRequest(GameProfileRequestEvent event) {
|
||||
Connection player = playerCache.getIfPresent(event.getConnection());
|
||||
if (player != null) {
|
||||
playerCache.invalidate(event.getConnection());
|
||||
|
||||
GameProfile profile = new GameProfile(
|
||||
player.javaUuid(),
|
||||
player.javaUsername(),
|
||||
Collections.emptyList()
|
||||
);
|
||||
// The texture properties addition is to fix the February 2 2022 Mojang authentication changes
|
||||
if (!config.sendFloodgateData() && !player.isLinked()) {
|
||||
profile = profile.addProperty(new Property("textures", "", ""));
|
||||
}
|
||||
event.setGameProfile(profile);
|
||||
Connection connection = connectionManager.connectionByPlatformIdentifier(event.getConnection());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameProfile profile = new GameProfile(
|
||||
connection.javaUuid(),
|
||||
connection.javaUsername(),
|
||||
Collections.emptyList()
|
||||
);
|
||||
// The texture properties addition is to fix the February 2 2022 Mojang authentication changes
|
||||
if (!config.sendFloodgateData() && !connection.isLinked()) {
|
||||
profile = profile.addProperty(new Property("textures", "", ""));
|
||||
}
|
||||
event.setGameProfile(profile);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onLogin(LoginEvent event) {
|
||||
if (event.getResult().isAllowed()) {
|
||||
Connection player = api.connectionByUuid(event.getPlayer().getUniqueId());
|
||||
if (player != null) {
|
||||
languageManager.loadLocale(player.languageCode());
|
||||
}
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onLogin(PostLoginEvent event) {
|
||||
Connection connection = api.connectionByPlatformIdentifier(event.getPlayer());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
languageManager.loadLocale(connection.languageCode());
|
||||
|
||||
// Depending on whether online-mode-kick-existing-players is enabled there are a few events
|
||||
// where there are two players online: PermissionSetupEvent and LoginEvent.
|
||||
// Only after LoginEvent the duplicated player is kicked. This is the first event after that
|
||||
connectionManager.addAcceptedConnection(connection);
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onDisconnect(DisconnectEvent event) {
|
||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||
connectionManager.removeConnection(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.velocity.player;
|
||||
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getField;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getFieldOfType;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue;
|
||||
import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.connection.Connection;
|
||||
import org.geysermc.floodgate.core.player.ConnectionManager;
|
||||
|
||||
@Singleton
|
||||
public class VelocityConnectionManager extends ConnectionManager {
|
||||
private static final Class<?> LOGIN_INBOUND_CONNECTION;
|
||||
private static final Field INITIAL_CONNECTION_DELEGATE;
|
||||
private static final Class<?> INITIAL_INBOUND_CONNECTION;
|
||||
private static final Class<?> MINECRAFT_CONNECTION;
|
||||
private static final Method GET_CONNECTION;
|
||||
private static final Field CHANNEL;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
AttributeKey<Connection> playerAttribute;
|
||||
|
||||
@Override
|
||||
protected @Nullable Object platformIdentifierOrConnectionFor(Object input) {
|
||||
if (input instanceof Player) {
|
||||
// ConnectedPlayer implements VelocityInboundConnection,
|
||||
// just like InitialInboundConnection
|
||||
return invoke(input, GET_CONNECTION);
|
||||
}
|
||||
|
||||
// LoginInboundConnection doesn't have a direct Channel reference,
|
||||
// but it does have an InitialInboundConnection reference
|
||||
if (LOGIN_INBOUND_CONNECTION.isInstance(input)) {
|
||||
return getValue(input, INITIAL_CONNECTION_DELEGATE);
|
||||
}
|
||||
|
||||
// InitialInboundConnection -> MinecraftConnection -> Channel -> FloodgateConnection attribute
|
||||
|
||||
if (INITIAL_INBOUND_CONNECTION.isInstance(input)) {
|
||||
return invoke(input, GET_CONNECTION);
|
||||
}
|
||||
if (MINECRAFT_CONNECTION.isInstance(input)) {
|
||||
return getValue(input, CHANNEL);
|
||||
}
|
||||
if (input instanceof Channel channel) {
|
||||
return channel.attr(playerAttribute).get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Channel channelFor(Object input) {
|
||||
var result = platformIdentifierOrConnectionFor(input);
|
||||
if (result instanceof Channel channel) {
|
||||
return channel;
|
||||
}
|
||||
return channelFor(result);
|
||||
}
|
||||
|
||||
static {
|
||||
LOGIN_INBOUND_CONNECTION = getPrefixedClass("connection.client.LoginInboundConnection");
|
||||
INITIAL_CONNECTION_DELEGATE = getField(LOGIN_INBOUND_CONNECTION, "delegate");
|
||||
|
||||
INITIAL_INBOUND_CONNECTION = getPrefixedClass("connection.client.InitialInboundConnection");
|
||||
MINECRAFT_CONNECTION = getPrefixedClass("connection.MinecraftConnection");
|
||||
|
||||
Class<?> velocityInboundConnection = getPrefixedClass("connection.util.VelocityInboundConnection");
|
||||
GET_CONNECTION = getMethod(velocityInboundConnection, "getConnection");
|
||||
|
||||
CHANNEL = getFieldOfType(MINECRAFT_CONNECTION, Channel.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user