1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2026-01-04 15:31:48 +00:00

Introduced connectionByPlatformIdentifier, removed guava usage

This commit is contained in:
Tim203
2023-06-25 16:00:48 +02:00
parent f47ff5e008
commit 01497ba86c
31 changed files with 544 additions and 340 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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();
}
}

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;
}
}