Skip to content

Commit

Permalink
Port reduce session server lookups (#509) to development
Browse files Browse the repository at this point in the history
Co-authored-by: Bridge <29434554+bridgelol@users.noreply.github.com>
  • Loading branch information
Tim203 and bridgelol committed May 18, 2024
1 parent 9746632 commit 0591630
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
Expand All @@ -49,6 +50,7 @@
import org.geysermc.floodgate.core.skin.SkinApplier;
import org.geysermc.floodgate.core.skin.SkinDataImpl;
import org.geysermc.floodgate.core.util.LanguageManager;
import org.geysermc.floodgate.core.util.MojangUtils;
import org.geysermc.floodgate.core.util.ReflectionUtils;

@Singleton
Expand All @@ -60,11 +62,13 @@ public final class BungeeListener implements Listener, McListener {
requireNonNull(PLAYER_NAME, "Initial name field cannot be null");
}

@Inject Plugin plugin;
@Inject BungeeConnectionManager connectionManager;
@Inject ProxyFloodgateConfig config;
@Inject SimpleFloodgateApi api;
@Inject LanguageManager languageManager;
@Inject SkinApplier skinApplier;
@Inject MojangUtils mojangUtils;

@Inject
@Named("kickMessageAttribute")
Expand Down Expand Up @@ -112,13 +116,26 @@ public void onLogin(LoginEvent event) {

@EventHandler(priority = EventPriority.LOWEST)
public void onPostLogin(PostLoginEvent event) {
// To fix the February 2 2022 Mojang authentication changes
if (!config.sendFloodgateData()) {
Connection connection = api.connectionByPlatformIdentifier(event.getPlayer());
if (connection != null && !connection.isLinked()) {
skinApplier.applySkin(connection, new SkinDataImpl("", ""));
}
Connection connection = api.connectionByPlatformIdentifier(event.getPlayer());
if (connection == null) {
return;
}

// Skin look up (on Spigot and friends) would result in it failing, so apply a default skin
if (!connection.isLinked()) {
skinApplier.applySkin(connection, SkinDataImpl.DEFAULT_SKIN);
return;
}

// Floodgate players are seen as offline mode players, meaning we have to look up
// the linked player's textures ourselves

event.registerIntent(plugin);

mojangUtils.skinFor(connection.javaUuid()).thenAccept(skin -> {
skinApplier.applySkin(connection, skin);
event.completeIntent(plugin);
});
}

@EventHandler(priority = EventPriority.HIGHEST)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ public void applySkin(@NonNull Connection connection, @NonNull SkinData skinData
SkinData currentSkin = currentSkin(properties);

SkinApplyEvent event = new SkinApplyEventImpl(connection, currentSkin, skinData);
event.cancelled(connection.isLinked());

eventBus.fire(event);

if (event.cancelled()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.geysermc.floodgate.core.http.minecraft;

import static io.micronaut.http.HttpHeaders.ACCEPT;
import static io.micronaut.http.HttpHeaders.USER_AGENT;

import io.micronaut.http.annotation.Get;
Expand All @@ -13,7 +12,6 @@

@Client("https://api.minecraftservices.com/minecraft")
@Header(name = USER_AGENT, value = "${http.userAgent}")
@Header(name = ACCEPT, value = "application/json")
public interface MinecraftClient {
@Get("/profile/lookup/name/{name}")
CompletableFuture<@Nullable ProfileResult> profileByName(@NonNull String name);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.geysermc.floodgate.core.http.mojang;

import io.micronaut.serde.annotation.Serdeable;
import jakarta.validation.constraints.NotNull;
import org.checkerframework.checker.nullness.qual.Nullable;

@Serdeable
public record ProfileProperty(@NotNull String name, @NotNull String value, @Nullable String signature) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.geysermc.floodgate.core.http.mojang;

import io.micronaut.serde.annotation.Serdeable;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;

@Serdeable
public record ProfileWithProperties(String id, String name, List<ProfileProperty> properties) {
public @Nullable ProfileProperty texture() {
for (ProfileProperty property : properties) {
if (property.name().equals("texture")) {
return property;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.geysermc.floodgate.core.http.mojang;

import static io.micronaut.http.HttpHeaders.USER_AGENT;

import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

@Client("https://sessionserver.mojang.com/session/minecraft")
@Header(name = USER_AGENT, value = "${http.userAgent}")
public interface SessionServerClient {
@Get("/profile/{uuid}?unsigned=false")
CompletableFuture<ProfileWithProperties> profileWithProperties(@NotNull UUID uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
import org.geysermc.floodgate.core.util.Constants;

public record SkinDataImpl(String value, String signature) implements SkinData {
public static final SkinData DEFAULT_SKIN = new SkinDataImpl(
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE,
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE
);

public SkinDataImpl(@NonNull String value, @MonotonicNonNull String signature) {
this.value = Objects.requireNonNull(value);
this.signature = Objects.requireNonNull(signature);
Expand Down
12 changes: 2 additions & 10 deletions core/src/main/java/org/geysermc/floodgate/core/util/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,18 @@ public final class Constants {

public static final int MAX_DEBUG_PACKET_COUNT = 20;
public static final boolean DEBUG_MODE = false;
public static final boolean PRINT_ALL_PACKETS = false;


public static final String USER_AGENT = "GeyserMC/Floodgate";
private static final String API_BASE_URL = "s://api.geysermc.org";
public static final String HEALTH_URL = "http" + API_BASE_URL + "/health";

public static final String LINK_INFO_URL = "https://link.geysermc.org/";
public static final String LATEST_DOWNLOAD_URL = "https://geysermc.org/download";

public static final String INTERNAL_ERROR_MESSAGE =
"An internal error happened while handling Floodgate data." +
" Try logging in again or contact a server administrator if the issue persists.";
public static final String UNSUPPORTED_DATA_VERSION =
"Received an unsupported Floodgate data version." +
" This Floodgate version is made for data version %s, received %s." +
" Make sure that Floodgate is up-to-date.";


public static final int HANDSHAKE_PACKET_ID = 0;
public static final int LOGIN_SUCCESS_PACKET_ID = 2;
public static final int SET_COMPRESSION_PACKET_ID = 3;
public static final String DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE = "ewogICJ0aW1lc3RhbXAiIDogMTcxNTcxNzM1NTI2MywKICAicHJvZmlsZUlkIiA6ICIyMWUzNjdkNzI1Y2Y0ZTNiYjI2OTJjNGEzMDBhNGRlYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJHZXlzZXJNQyIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS8zMWY0NzdlYjFhN2JlZWU2MzFjMmNhNjRkMDZmOGY2OGZhOTNhMzM4NmQwNDQ1MmFiMjdmNDNhY2RmMWI2MGNiIgogICAgfQogIH0KfQ";
public static final String DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE = "dFKIZ5d6vNqCSe1IFGiVLjt3cnW8qh4qNP2umg9zqkX9bvAQawuR1iuO1kCD/+ye8A6GQFv2wRCdxdrjp5+Vrr0SsWqMnsYDN8cEg6CD18mAnaKI1TYDuGbdJaqLyGqN5wqSMdHxchs9iovFkde5ir4aYdvHkA11vOTi11L4kUzETGzJ4iKVuZOv4dq+B7wFAWqp4n8QZfhixyvemFazQHlLmxnuhU+jhpZMvYY9MAaRAJonfy/wJe9LymbTe0EJ8N+NwZQDrEUzgfBFo4OIGDqRZwvydInCqkjhPMtHCSL25VOKwcFocYpRYbk4eIKM4CLjYlBiQGki+XKsPaljwjVhnT0jUupSf7yraGb3T0CsVBjhDbIIIp9nytlbO0GvxHu0TzYjkr4Iji0do5jlCKQ/OasXcL21wd6ozw0t1QZnnzxi9ewSuyYVY9ErmWdkww1OtCIgJilceEBwNAB8+mhJ062WFaYPgJQAmOREM8InW33dbbeENMFhQi4LIO5P7p9ye3B4Lrwm20xtd9wJk3lewzcs8ezh0LUF6jPSDQDivgSKU49mLCTmOi+WZh8zKjjxfVEtNZON2W+3nct0LiWBVsQ55HzlvF0FFxuRVm6pxi6MQK2ernv3DQl0hUqyQ1+RV9nfZXTQOAUzwLjKx3t2zKqyZIiNEKLE+iAXrsE=";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.geysermc.floodgate.core.util;

import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
import org.geysermc.floodgate.core.http.mojang.SessionServerClient;
import org.geysermc.floodgate.core.logger.FloodgateLogger;
import org.geysermc.floodgate.core.skin.SkinDataImpl;

@Singleton
public final class MojangUtils {
@Inject SessionServerClient sessionClient;
@Inject FloodgateLogger logger;

public CompletableFuture<SkinData> skinFor(UUID uuid) {
return sessionClient.profileWithProperties(uuid)
.thenApply(skin -> {
var texture = skin.texture();
if (texture == null) {
return SkinDataImpl.DEFAULT_SKIN;
}
return new SkinDataImpl(texture.value(), texture.signature());
}).exceptionally(exception -> {
logger.debug("Unexpected skin fetch error for " + uuid, exception);
return SkinDataImpl.DEFAULT_SKIN;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import static org.geysermc.floodgate.core.util.ReflectionUtils.setValue;

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import java.lang.reflect.InvocationTargetException;
Expand All @@ -41,10 +42,17 @@
import org.geysermc.floodgate.core.connection.FloodgateDataHandler;
import org.geysermc.floodgate.core.connection.FloodgateDataHandler.HandleResult;
import org.geysermc.floodgate.core.logger.FloodgateLogger;
import org.geysermc.floodgate.core.util.Constants;
import org.geysermc.floodgate.spigot.util.ClassNames;
import org.geysermc.floodgate.spigot.util.ProxyUtils;

public final class SpigotDataHandler extends CommonNettyDataHandler {
private static final Property DEFAULT_TEXTURE_PROPERTY = new Property(
"textures",
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE,
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE
);

private Object networkManager;
private Connection connection;
private boolean proxyData;
Expand Down Expand Up @@ -182,6 +190,14 @@ private boolean checkAndHandleLogin(Object packet) throws Exception {
}

GameProfile gameProfile = new GameProfile(connection.javaUuid(), connection.javaUsername());

if (!connection.isLinked()) {
// Otherwise game server will try to fetch the skin from Mojang.
// No need to worry that this overrides proxy data, because those won't reach this
// method / are already removed (in the case of username validation)
gameProfile.getProperties().put("textures", DEFAULT_TEXTURE_PROPERTY);
}

setValue(packetListener, ClassNames.LOGIN_PROFILE, gameProfile);

// we have to fake the offline player (login) cycle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,29 @@
package org.geysermc.floodgate.spigot.listener;

import com.destroystokyo.paper.event.profile.PreFillProfileEvent;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.destroystokyo.paper.profile.ProfileProperty;
import io.micronaut.context.annotation.Requires;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.geysermc.api.connection.Connection;
import org.geysermc.floodgate.core.api.SimpleFloodgateApi;
import org.geysermc.floodgate.core.listener.McListener;
import org.geysermc.floodgate.core.util.Constants;

@Requires(classes = PreFillProfileEvent.class)
@Singleton
public final class PaperProfileListener implements Listener, McListener {
private static final ProfileProperty DEFAULT_TEXTURE_PROPERTY = new ProfileProperty(
"textures",
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE,
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE
);

@Inject SimpleFloodgateApi api;

@EventHandler
Expand All @@ -67,26 +71,8 @@ public void onFill(PreFillProfileEvent event) {
}

Set<ProfileProperty> properties = new HashSet<>(event.getPlayerProfile().getProperties());
properties.add(new ProfileProperty("textures", "", ""));
event.setProperties(properties);
}
properties.add(DEFAULT_TEXTURE_PROPERTY);

@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Connection connection = api.connectionByPlatformIdentifier(player);
if (connection == null || connection.isLinked()) {
return;
}

PlayerProfile profile = player.getPlayerProfile();
if (profile.getProperties().stream().noneMatch(
prop -> "textures".equals(prop.getName()) && prop.getValue().isEmpty()
&& prop.getSignature() != null && prop.getSignature().isEmpty())) {
return;
}

profile.removeProperty("textures");
player.setPlayerProfile(profile);
event.setProperties(properties);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
Expand Down Expand Up @@ -62,7 +63,8 @@ public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promi

// get the FloodgatePlayer from the ConnectedPlayer
MinecraftConnection minecraftConnection = (MinecraftConnection) ctx.pipeline().get("handler");
Player velocityPlayer = (Player) minecraftConnection.getAssociation();
VelocityServerConnection serverConnection = (VelocityServerConnection) minecraftConnection.getAssociation();
Player velocityPlayer = serverConnection.getPlayer();

Connection connection = api.connectionByPlatformIdentifier(velocityPlayer);
if (connection != null) {
Expand Down
Loading

0 comments on commit 0591630

Please sign in to comment.