From 4483ce3a328cb0cd39a73dc19f78b2a1a164fd63 Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 03:19:09 +0200 Subject: [PATCH 01/11] fix: add default skin to gameprofiles --- .../floodgate/addon/data/SpigotDataHandler.java | 8 ++++++++ .../geysermc/floodgate/listener/VelocityListener.java | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 36f34e9d..aa1ba1fc 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -29,6 +29,7 @@ import static org.geysermc.floodgate.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; @@ -38,6 +39,7 @@ import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; import org.geysermc.floodgate.util.ClassNames; +import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.ProxyUtils; public final class SpigotDataHandler extends CommonDataHandler { @@ -171,6 +173,12 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { player.getCorrectUniqueId(), player.getCorrectUsername() ); + // Otherwise game server will try to fetch the skin from Mojang + gameProfile.getProperties().put( + "textures", + new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, "") + ); + // we have to fake the offline player (login) cycle if (ClassNames.IS_PRE_1_20_2) { diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index 8000f520..dd14d556 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -48,7 +48,9 @@ import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import net.kyori.adventure.text.Component; @@ -56,6 +58,7 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.LanguageManager; public final class VelocityListener { @@ -147,12 +150,9 @@ public void onGameProfileRequest(GameProfileRequestEvent event) { GameProfile profile = new GameProfile( player.getCorrectUniqueId(), player.getCorrectUsername(), - Collections.emptyList() + List.of(new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, "")) // Otherwise game server will try to fetch the skin from Mojang ); - // The texture properties addition is to fix the February 2 2022 Mojang authentication changes - if (!config.isSendFloodgateData() && !player.isLinked()) { - profile = profile.addProperty(new Property("textures", "", "")); - } + event.setGameProfile(profile); } } From f2c9d79611b4c17de9772ff85fe1e38ee0dfc9b2 Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 03:45:20 +0200 Subject: [PATCH 02/11] fix: add signatures by default to prevent issues --- .../geysermc/floodgate/util/Constants.java | 3 ++ .../addon/data/SpigotDataHandler.java | 5 +++- .../floodgate/listener/VelocityListener.java | 29 +++++++------------ 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/core/src/main/templates/org/geysermc/floodgate/util/Constants.java b/core/src/main/templates/org/geysermc/floodgate/util/Constants.java index 62cec467..99f2348f 100644 --- a/core/src/main/templates/org/geysermc/floodgate/util/Constants.java +++ b/core/src/main/templates/org/geysermc/floodgate/util/Constants.java @@ -70,4 +70,7 @@ public final class Constants { 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 = "ewogICJ0aW1lc3RhbXAiIDogMTcxNTMwNTM4NDQwOSwKICAicHJvZmlsZUlkIiA6ICJjZDZkODYwMzRhMWI0YTgxYjhhN2E2Y2IyM2Y5MjI4MCIsCiAgInByb2ZpbGVOYW1lIiA6ICJKdWVsbGU5MTc5IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzMxZjQ3N2ViMWE3YmVlZTYzMWMyY2E2NGQwNmY4ZjY4ZmE5M2EzMzg2ZDA0NDUyYWIyN2Y0M2FjZGYxYjYwY2IiCiAgICB9CiAgfQp9"; + public static final String DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE = "gUiXhG+rqQRpY2LEI3V81xOKtmuvqxZUZsqm5ojgTnUnWCAPY39H3SZV/p4C3+x4Yp1pBQCR24B1WCAXT8s1Pc3PPf0ptav2TOrn0+B40XJYlabx7m1Qd5tNmbzAAuZpmQ2kdprQom/KmtYJnxXgMHAEuOy4oW3n736ZTYlvDSZtXNcwElOIbg0Zq1Dis0Qm54MazDpYdC8VPm1twrrR6DAHSJeTAx99NBcVgKDEwnFEO+4ch4ATD0AboJq2Alfa81b2BZ8ko02rB6s4WP4/qG1yyNBanO1FnSqqNPoNXQT/+og6dWOW62dcu9OXAdTIaKJ9P+ER9Yyo6Tv5eQGSB7VikZIollYN2OZ2TPMyqqEouHjKAggQEdOb/avid08YMveQr9c7yW1Ay2JAF6BH6D0tmdZ4WQWZUT6xR98o88sUAIhSGbds+h8PbJdWhGa1MDT+hNEcUmWJ0Mui8CBWIrhJAzppgDbsZlX9mWjQEwJAtlcUe9BEeZV3EhriPr9dCg867ojV5yXjrv+G6fpPUy+Zkkg/u38APnGBsWtPU8jDEpLtsGjbe6L7v7AvJuRv4Q35YuRD2j+UXZROxmkVKc4/PV/3SC15ePhU397gicQ5E41kvjikIdYYdkjQoj2G2esEZmvqCWzAm1czPeH+FiemCYgscM2QcFe+rBV0GJw="; } diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index aa1ba1fc..3e046b29 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -176,7 +176,10 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { // Otherwise game server will try to fetch the skin from Mojang gameProfile.getProperties().put( "textures", - new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, "") + new Property("textures", + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ) ); // we have to fake the offline player (login) cycle diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index dd14d556..a5405267 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -48,8 +48,6 @@ import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -72,14 +70,12 @@ public final class VelocityListener { INITIAL_MINECRAFT_CONNECTION = getFieldOfType(initialConnection, minecraftConnection); // Since Velocity 3.1.0 - Class loginInboundConnection = - getPrefixedClassSilently("connection.client.LoginInboundConnection"); + 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" - ); + Objects.requireNonNull(INITIAL_CONNECTION_DELEGATE, + "initial inbound connection delegate cannot be null"); } else { INITIAL_CONNECTION_DELEGATE = null; } @@ -87,11 +83,8 @@ public final class VelocityListener { CHANNEL = getFieldOfType(minecraftConnection, Channel.class); } - private final Cache playerCache = - CacheBuilder.newBuilder() - .maximumSize(500) - .expireAfterAccess(20, TimeUnit.SECONDS) - .build(); + private final Cache playerCache = CacheBuilder.newBuilder().maximumSize( + 500).expireAfterAccess(20, TimeUnit.SECONDS).build(); @Inject private ProxyFloodgateConfig config; @Inject private ProxyFloodgateApi api; @@ -130,8 +123,7 @@ public void onPreLogin(PreLoginEvent event) { if (kickMessage != null) { event.setResult( - PreLoginEvent.PreLoginComponentResult.denied(Component.text(kickMessage)) - ); + PreLoginEvent.PreLoginComponentResult.denied(Component.text(kickMessage))); return; } @@ -147,10 +139,11 @@ public void onGameProfileRequest(GameProfileRequestEvent event) { if (player != null) { playerCache.invalidate(event.getConnection()); - GameProfile profile = new GameProfile( - player.getCorrectUniqueId(), + GameProfile profile = new GameProfile(player.getCorrectUniqueId(), player.getCorrectUsername(), - List.of(new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, "")) // Otherwise game server will try to fetch the skin from Mojang + List.of(new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE)) + // Otherwise game server will try to fetch the skin from Mojang ); event.setGameProfile(profile); From a8a79991f7fe8cb7ecb0c69a49e42b905308bf8a Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 15:15:48 +0200 Subject: [PATCH 03/11] cleanup --- .../addon/data/SpigotDataHandler.java | 20 ++++++----- .../floodgate/listener/VelocityListener.java | 33 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 3e046b29..0660099a 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -43,6 +43,13 @@ import org.geysermc.floodgate.util.ProxyUtils; public final class SpigotDataHandler extends CommonDataHandler { + + 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 FloodgatePlayer player; private boolean proxyData; @@ -70,10 +77,10 @@ protected Object setHostname(Object handshakePacket, String hostname) throws Ill // 1.20.2 and above try { Object[] components = new Object[]{ - ClassNames.HANDSHAKE_PROTOCOL.get(handshakePacket), - hostname, - ClassNames.HANDSHAKE_PORT.get(handshakePacket), - ClassNames.HANDSHAKE_INTENTION.get(handshakePacket) + ClassNames.HANDSHAKE_PROTOCOL.get(handshakePacket), + hostname, + ClassNames.HANDSHAKE_PORT.get(handshakePacket), + ClassNames.HANDSHAKE_INTENTION.get(handshakePacket) }; return ClassNames.HANDSHAKE_PACKET_CONSTRUCTOR.newInstance(components); @@ -176,10 +183,7 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { // Otherwise game server will try to fetch the skin from Mojang gameProfile.getProperties().put( "textures", - new Property("textures", - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE - ) + DEFAULT_TEXTURE_PROPERTY ); // we have to fake the offline player (login) cycle diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index a5405267..cc9507c6 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -63,6 +63,7 @@ public final class VelocityListener { private static final Field INITIAL_MINECRAFT_CONNECTION; private static final Field INITIAL_CONNECTION_DELEGATE; private static final Field CHANNEL; + private static final Property DEFAULT_TEXTURE_PROPERTY; static { Class initialConnection = getPrefixedClass("connection.client.InitialInboundConnection"); @@ -70,21 +71,31 @@ public final class VelocityListener { INITIAL_MINECRAFT_CONNECTION = getFieldOfType(initialConnection, minecraftConnection); // Since Velocity 3.1.0 - Class loginInboundConnection = getPrefixedClassSilently( - "connection.client.LoginInboundConnection"); + 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"); + Objects.requireNonNull( + INITIAL_CONNECTION_DELEGATE, + "initial inbound connection delegate cannot be null" + ); } else { INITIAL_CONNECTION_DELEGATE = null; } CHANNEL = getFieldOfType(minecraftConnection, Channel.class); + DEFAULT_TEXTURE_PROPERTY = new Property( + "textures", + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ); } - private final Cache playerCache = CacheBuilder.newBuilder().maximumSize( - 500).expireAfterAccess(20, TimeUnit.SECONDS).build(); + private final Cache playerCache = + CacheBuilder.newBuilder() + .maximumSize(500) + .expireAfterAccess(20, TimeUnit.SECONDS) + .build(); @Inject private ProxyFloodgateConfig config; @Inject private ProxyFloodgateApi api; @@ -123,7 +134,8 @@ public void onPreLogin(PreLoginEvent event) { if (kickMessage != null) { event.setResult( - PreLoginEvent.PreLoginComponentResult.denied(Component.text(kickMessage))); + PreLoginEvent.PreLoginComponentResult.denied(Component.text(kickMessage)) + ); return; } @@ -139,11 +151,10 @@ public void onGameProfileRequest(GameProfileRequestEvent event) { if (player != null) { playerCache.invalidate(event.getConnection()); - GameProfile profile = new GameProfile(player.getCorrectUniqueId(), + GameProfile profile = new GameProfile( + player.getCorrectUniqueId(), player.getCorrectUsername(), - List.of(new Property("textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE)) - // Otherwise game server will try to fetch the skin from Mojang + List.of(DEFAULT_TEXTURE_PROPERTY) // Otherwise game server will try to fetch the skin from Mojang ); event.setGameProfile(profile); From 33fd6ac3e86addf1909fbcad19f24b7291021fda Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 15:21:11 +0200 Subject: [PATCH 04/11] no longer apply empty textures --- .../geysermc/floodgate/listener/BungeeListener.java | 6 +++++- .../floodgate/listener/PaperProfileListener.java | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 9723cf58..20450a27 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -49,6 +49,7 @@ import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinDataImpl; +import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.ReflectionUtils; @@ -131,7 +132,10 @@ public void onPostLogin(PostLoginEvent event) { if (!config.isSendFloodgateData()) { FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); if (player != null && !player.isLinked()) { - skinApplier.applySkin(player, new SkinDataImpl("", "")); + skinApplier.applySkin(player, new SkinDataImpl( + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE + )); } } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java b/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java index 29ade2ec..9d63058a 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java @@ -38,6 +38,7 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.util.Constants; public final class PaperProfileListener implements Listener { @Inject private SimpleFloodgateApi api; @@ -62,7 +63,14 @@ public void onFill(PreFillProfileEvent event) { } Set properties = new HashSet<>(event.getPlayerProfile().getProperties()); - properties.add(new ProfileProperty("textures", "", "")); + properties.add( + new ProfileProperty( + "textures", + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ) + ); + event.setProperties(properties); } From e48fe8bd83d0be3d152d8cdd0574e90c236db631 Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 15:31:53 +0200 Subject: [PATCH 05/11] revert formatting change --- .../geysermc/floodgate/addon/data/SpigotDataHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 0660099a..8203056f 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -77,10 +77,10 @@ protected Object setHostname(Object handshakePacket, String hostname) throws Ill // 1.20.2 and above try { Object[] components = new Object[]{ - ClassNames.HANDSHAKE_PROTOCOL.get(handshakePacket), - hostname, - ClassNames.HANDSHAKE_PORT.get(handshakePacket), - ClassNames.HANDSHAKE_INTENTION.get(handshakePacket) + ClassNames.HANDSHAKE_PROTOCOL.get(handshakePacket), + hostname, + ClassNames.HANDSHAKE_PORT.get(handshakePacket), + ClassNames.HANDSHAKE_INTENTION.get(handshakePacket) }; return ClassNames.HANDSHAKE_PACKET_CONSTRUCTOR.newInstance(components); From 92f29a707847d7e6a9a86833529a764e5eba31a4 Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 15:46:56 +0200 Subject: [PATCH 06/11] fix(spigot): linked player textures --- .../floodgate/addon/data/SpigotDataHandler.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 8203056f..5613d9e9 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -180,11 +180,13 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { player.getCorrectUniqueId(), player.getCorrectUsername() ); - // Otherwise game server will try to fetch the skin from Mojang - gameProfile.getProperties().put( - "textures", - DEFAULT_TEXTURE_PROPERTY - ); + if (!player.isLinked()) { + // Otherwise game server will try to fetch the skin from Mojang + gameProfile.getProperties().put( + "textures", + DEFAULT_TEXTURE_PROPERTY + ); + } // we have to fake the offline player (login) cycle From b31791af2225b18ce1c6457e5e9a2d81a7fba73b Mon Sep 17 00:00:00 2001 From: bridge Date: Fri, 10 May 2024 16:33:35 +0200 Subject: [PATCH 07/11] fix(velocity): linked player textures --- .../geysermc/floodgate/skin/SkinDataImpl.java | 6 ++ .../geysermc/floodgate/util/MojangUtils.java | 99 +++++++++++++++++++ .../floodgate/listener/VelocityListener.java | 23 ++++- 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java diff --git a/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java b/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java index 9f44af79..3d37ac16 100644 --- a/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java +++ b/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java @@ -29,6 +29,7 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; +import org.geysermc.floodgate.util.Constants; public class SkinDataImpl implements SkinData { private final String value; @@ -55,4 +56,9 @@ public static SkinData from(JsonObject data) { public @NonNull String signature() { return signature; } + + public static final SkinData DEFAULT_SKIN = new SkinDataImpl( + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ); } diff --git a/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java new file mode 100644 index 00000000..31e3648f --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019-2024 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.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import lombok.NonNull; +import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; +import org.geysermc.floodgate.skin.SkinDataImpl; +import org.geysermc.floodgate.util.HttpClient.HttpResponse; + +public class MojangUtils { + + private static final String SESSION_SERVER = + "https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false"; + + private static final Cache SKIN_CACHE = CacheBuilder.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .maximumSize(500) + .build(); + + public static @NonNull SkinData getSkinCached( + HttpClient httpClient, + UUID playerId + ) throws ExecutionException { + return SKIN_CACHE.get(playerId, () -> getSkin(httpClient, playerId)); + } + + public static @NonNull SkinData getSkin(HttpClient httpClient, UUID playerId) { + final HttpResponse httpResponse = httpClient.get(String.format(SESSION_SERVER, playerId.toString())); + + if (httpResponse.getHttpCode() != 200) { + return SkinDataImpl.DEFAULT_SKIN; + } + + JsonObject response = httpResponse.getResponse(); + + if (response == null) { + return SkinDataImpl.DEFAULT_SKIN; + } + + JsonArray properties = response.getAsJsonArray("properties"); + + if (properties.size() == 0) { + return SkinDataImpl.DEFAULT_SKIN; + } + + for (JsonElement property : properties) { + if (!property.isJsonObject()) { + continue; + } + + JsonObject propertyObject = property.getAsJsonObject(); + + if (!propertyObject.has("name") + || !propertyObject.has("value") + || !propertyObject.has("signature") + || !propertyObject.get("name").getAsString().equals("textures")) { + continue; + } + + return new SkinDataImpl( + propertyObject.get("value").getAsString(), + propertyObject.get("signature").getAsString() + ); + } + + return SkinDataImpl.DEFAULT_SKIN; + } +} diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index cc9507c6..e25a6139 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -48,16 +48,21 @@ import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; +import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.ProxyFloodgateApi; +import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.util.Constants; +import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.LanguageManager; +import org.geysermc.floodgate.util.MojangUtils; public final class VelocityListener { private static final Field INITIAL_MINECRAFT_CONNECTION; @@ -110,6 +115,9 @@ public final class VelocityListener { @Named("kickMessageAttribute") private AttributeKey kickMessageAttribute; + @Inject + private HttpClient httpClient; + @Subscribe(order = PostOrder.EARLY) public void onPreLogin(PreLoginEvent event) { FloodgatePlayer player = null; @@ -154,9 +162,22 @@ public void onGameProfileRequest(GameProfileRequestEvent event) { GameProfile profile = new GameProfile( player.getCorrectUniqueId(), player.getCorrectUsername(), - List.of(DEFAULT_TEXTURE_PROPERTY) // Otherwise game server will try to fetch the skin from Mojang + player.isLinked() ? Collections.emptyList() : List.of(DEFAULT_TEXTURE_PROPERTY) // Otherwise game server will try to fetch the skin from Mojang ); + if (player.isLinked()) { + // Do texture lookup to session server + try { + SkinData skin = MojangUtils.getSkinCached(httpClient, + player.getJavaUniqueId()); + + profile.addProperty(new Property("textures", skin.value(), skin.signature())); + } catch (ExecutionException e) { + logger.error("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", e); + profile.addProperty(DEFAULT_TEXTURE_PROPERTY); + } + } + event.setGameProfile(profile); } } From 1c5953d8b3e60eec9531af310e00f0191e7e92ef Mon Sep 17 00:00:00 2001 From: bridge Date: Mon, 13 May 2024 16:06:02 +0200 Subject: [PATCH 08/11] fix(bungeecord): apply linked textures --- .../floodgate/listener/BungeeListener.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 20450a27..09b2cd08 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -33,6 +33,8 @@ import io.netty.util.AttributeKey; import java.lang.reflect.Field; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -50,7 +52,9 @@ import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinDataImpl; import org.geysermc.floodgate.util.Constants; +import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.LanguageManager; +import org.geysermc.floodgate.util.MojangUtils; import org.geysermc.floodgate.util.ReflectionUtils; @SuppressWarnings("ConstantConditions") @@ -81,6 +85,8 @@ public final class BungeeListener implements Listener { @Named("kickMessageAttribute") private AttributeKey kickMessageAttribute; + @Inject private HttpClient httpClient; + @EventHandler(priority = EventPriority.LOWEST) public void onPreLogin(PreLoginEvent event) { // well, no reason to check if the player will be kicked anyway @@ -131,12 +137,35 @@ public void onPostLogin(PostLoginEvent event) { // To fix the February 2 2022 Mojang authentication changes if (!config.isSendFloodgateData()) { FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); - if (player != null && !player.isLinked()) { + + if (player == null) { + return; + } + + if (!player.isLinked()) { skinApplier.applySkin(player, new SkinDataImpl( Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE )); + + return; } + + CompletableFuture.supplyAsync(() -> { + try { + return MojangUtils.getSkinCached(httpClient, player.getJavaUniqueId()); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + }).thenAccept(skin -> skinApplier.applySkin(player, skin)) + .exceptionally(e -> { + logger.error("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", e); + skinApplier.applySkin(player, new SkinDataImpl( + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE + )); + return null; + }); } } From 128519a20adb5ffda807580c03aac1ffc78c462d Mon Sep 17 00:00:00 2001 From: Tim203 Date: Tue, 14 May 2024 22:32:51 +0200 Subject: [PATCH 09/11] Made the MojangUtils class instance based, removed some unneeded code --- .../floodgate/listener/BungeeListener.java | 34 +++++----------- .../geysermc/floodgate/skin/SkinDataImpl.java | 10 ++--- .../geysermc/floodgate/util/MojangUtils.java | 39 +++++++++++++------ .../geysermc/floodgate/util/Constants.java | 7 +++- .../addon/data/SpigotDataHandler.java | 6 +-- .../listener/PaperProfileListener.java | 36 ++++------------- .../floodgate/listener/VelocityListener.java | 37 ++++++++++-------- 7 files changed, 75 insertions(+), 94 deletions(-) diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 09b2cd08..0de97418 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -33,8 +33,6 @@ import io.netty.util.AttributeKey; import java.lang.reflect.Field; import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -51,8 +49,6 @@ import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinDataImpl; -import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.MojangUtils; import org.geysermc.floodgate.util.ReflectionUtils; @@ -85,7 +81,7 @@ public final class BungeeListener implements Listener { @Named("kickMessageAttribute") private AttributeKey kickMessageAttribute; - @Inject private HttpClient httpClient; + @Inject private MojangUtils mojangUtils; @EventHandler(priority = EventPriority.LOWEST) public void onPreLogin(PreLoginEvent event) { @@ -134,7 +130,6 @@ 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.isSendFloodgateData()) { FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); @@ -142,28 +137,19 @@ public void onPostLogin(PostLoginEvent event) { return; } - if (!player.isLinked()) { - skinApplier.applySkin(player, new SkinDataImpl( - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE - )); + // Floodgate players are seen as offline mode players, meaning we have to look up + // the linked player's textures ourselves + if (!player.isLinked()) { + skinApplier.applySkin(player, SkinDataImpl.DEFAULT_SKIN); return; } - CompletableFuture.supplyAsync(() -> { - try { - return MojangUtils.getSkinCached(httpClient, player.getJavaUniqueId()); - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - }).thenAccept(skin -> skinApplier.applySkin(player, skin)) - .exceptionally(e -> { - logger.error("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", e); - skinApplier.applySkin(player, new SkinDataImpl( - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE - )); + mojangUtils.asyncSkinFor(player.getJavaUniqueId()) + .thenAccept(skin -> skinApplier.applySkin(player, skin)) + .exceptionally(exception -> { + logger.debug("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", exception); + skinApplier.applySkin(player, SkinDataImpl.DEFAULT_SKIN); return null; }); } diff --git a/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java b/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java index 3d37ac16..1e2247e2 100644 --- a/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java +++ b/core/src/main/java/org/geysermc/floodgate/skin/SkinDataImpl.java @@ -32,6 +32,11 @@ import org.geysermc.floodgate.util.Constants; public class SkinDataImpl implements SkinData { + public static final SkinData DEFAULT_SKIN = new SkinDataImpl( + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ); + private final String value; private final String signature; @@ -56,9 +61,4 @@ public static SkinData from(JsonObject data) { public @NonNull String signature() { return signature; } - - public static final SkinData DEFAULT_SKIN = new SkinDataImpl( - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE - ); } diff --git a/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java index 31e3648f..73845cc5 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java @@ -30,33 +30,48 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Named; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import lombok.NonNull; import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; import org.geysermc.floodgate.skin.SkinDataImpl; import org.geysermc.floodgate.util.HttpClient.HttpResponse; +@Singleton public class MojangUtils { - - private static final String SESSION_SERVER = - "https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false"; - - private static final Cache SKIN_CACHE = CacheBuilder.newBuilder() + private final Cache SKIN_CACHE = CacheBuilder.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .maximumSize(500) .build(); - public static @NonNull SkinData getSkinCached( - HttpClient httpClient, - UUID playerId - ) throws ExecutionException { - return SKIN_CACHE.get(playerId, () -> getSkin(httpClient, playerId)); + @Inject private HttpClient httpClient; + @Inject + @Named("commonPool") + private ExecutorService commonPool; + + public @NonNull SkinData skinFor(UUID playerId) throws ExecutionException { + return SKIN_CACHE.get(playerId, () -> fetchSkinFor(playerId)); + } + + public CompletableFuture<@NonNull SkinData> asyncSkinFor(UUID playerId) { + return CompletableFuture.supplyAsync(() -> { + try { + return SKIN_CACHE.get(playerId, () -> fetchSkinFor(playerId)); + } catch (ExecutionException exception) { + throw new RuntimeException(exception.getCause()); + } + }, commonPool); } - public static @NonNull SkinData getSkin(HttpClient httpClient, UUID playerId) { - final HttpResponse httpResponse = httpClient.get(String.format(SESSION_SERVER, playerId.toString())); + private @NonNull SkinData fetchSkinFor(UUID playerId) { + HttpResponse httpResponse = httpClient.get( + String.format(Constants.PROFILE_WITH_PROPERTIES_URL, playerId.toString())); if (httpResponse.getHttpCode() != 200) { return SkinDataImpl.DEFAULT_SKIN; diff --git a/core/src/main/templates/org/geysermc/floodgate/util/Constants.java b/core/src/main/templates/org/geysermc/floodgate/util/Constants.java index 99f2348f..1eec715a 100644 --- a/core/src/main/templates/org/geysermc/floodgate/util/Constants.java +++ b/core/src/main/templates/org/geysermc/floodgate/util/Constants.java @@ -56,6 +56,9 @@ public final class Constants { public static final String LATEST_VERSION_URL = "https://download.geysermc.org/v2/projects/%s/versions/latest/builds/latest"; + public static final String PROFILE_WITH_PROPERTIES_URL = + "https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false"; + public static final String NTP_SERVER = "time.cloudflare.com"; public static final String INTERNAL_ERROR_MESSAGE = @@ -71,6 +74,6 @@ public final class Constants { 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 = "ewogICJ0aW1lc3RhbXAiIDogMTcxNTMwNTM4NDQwOSwKICAicHJvZmlsZUlkIiA6ICJjZDZkODYwMzRhMWI0YTgxYjhhN2E2Y2IyM2Y5MjI4MCIsCiAgInByb2ZpbGVOYW1lIiA6ICJKdWVsbGU5MTc5IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzMxZjQ3N2ViMWE3YmVlZTYzMWMyY2E2NGQwNmY4ZjY4ZmE5M2EzMzg2ZDA0NDUyYWIyN2Y0M2FjZGYxYjYwY2IiCiAgICB9CiAgfQp9"; - public static final String DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE = "gUiXhG+rqQRpY2LEI3V81xOKtmuvqxZUZsqm5ojgTnUnWCAPY39H3SZV/p4C3+x4Yp1pBQCR24B1WCAXT8s1Pc3PPf0ptav2TOrn0+B40XJYlabx7m1Qd5tNmbzAAuZpmQ2kdprQom/KmtYJnxXgMHAEuOy4oW3n736ZTYlvDSZtXNcwElOIbg0Zq1Dis0Qm54MazDpYdC8VPm1twrrR6DAHSJeTAx99NBcVgKDEwnFEO+4ch4ATD0AboJq2Alfa81b2BZ8ko02rB6s4WP4/qG1yyNBanO1FnSqqNPoNXQT/+og6dWOW62dcu9OXAdTIaKJ9P+ER9Yyo6Tv5eQGSB7VikZIollYN2OZ2TPMyqqEouHjKAggQEdOb/avid08YMveQr9c7yW1Ay2JAF6BH6D0tmdZ4WQWZUT6xR98o88sUAIhSGbds+h8PbJdWhGa1MDT+hNEcUmWJ0Mui8CBWIrhJAzppgDbsZlX9mWjQEwJAtlcUe9BEeZV3EhriPr9dCg867ojV5yXjrv+G6fpPUy+Zkkg/u38APnGBsWtPU8jDEpLtsGjbe6L7v7AvJuRv4Q35YuRD2j+UXZROxmkVKc4/PV/3SC15ePhU397gicQ5E41kvjikIdYYdkjQoj2G2esEZmvqCWzAm1czPeH+FiemCYgscM2QcFe+rBV0GJw="; + 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="; } diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 5613d9e9..08f49125 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -43,7 +43,6 @@ import org.geysermc.floodgate.util.ProxyUtils; public final class SpigotDataHandler extends CommonDataHandler { - private static final Property DEFAULT_TEXTURE_PROPERTY = new Property( "textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, @@ -182,10 +181,7 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { if (!player.isLinked()) { // Otherwise game server will try to fetch the skin from Mojang - gameProfile.getProperties().put( - "textures", - DEFAULT_TEXTURE_PROPERTY - ); + gameProfile.getProperties().put("textures", DEFAULT_TEXTURE_PROPERTY); } // we have to fake the offline player (login) cycle diff --git a/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java b/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java index 9d63058a..836b9fb2 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/PaperProfileListener.java @@ -26,21 +26,24 @@ package org.geysermc.floodgate.listener; import com.destroystokyo.paper.event.profile.PreFillProfileEvent; -import com.destroystokyo.paper.profile.PlayerProfile; import com.destroystokyo.paper.profile.ProfileProperty; import com.google.inject.Inject; 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.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.util.Constants; public final class PaperProfileListener implements Listener { + private static final ProfileProperty DEFAULT_TEXTURE_PROPERTY = new ProfileProperty( + "textures", + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, + Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE + ); + @Inject private SimpleFloodgateApi api; @EventHandler @@ -63,33 +66,8 @@ public void onFill(PreFillProfileEvent event) { } Set properties = new HashSet<>(event.getPlayerProfile().getProperties()); - properties.add( - new ProfileProperty( - "textures", - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, - Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE - ) - ); + properties.add(DEFAULT_TEXTURE_PROPERTY); event.setProperties(properties); } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - Player bukkitPlayer = event.getPlayer(); - FloodgatePlayer player = api.getPlayer(bukkitPlayer.getUniqueId()); - if (player == null || player.isLinked()) { - return; - } - - PlayerProfile profile = bukkitPlayer.getPlayerProfile(); - if (profile.getProperties().stream().noneMatch( - prop -> "textures".equals(prop.getName()) && prop.getValue().isEmpty() - && prop.getSignature() != null && prop.getSignature().isEmpty())) { - return; - } - - profile.removeProperty("textures"); - bukkitPlayer.setPlayerProfile(profile); - } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index e25a6139..a9369916 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -48,7 +48,7 @@ import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; @@ -60,7 +60,6 @@ import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.MojangUtils; @@ -89,6 +88,7 @@ public final class VelocityListener { } CHANNEL = getFieldOfType(minecraftConnection, Channel.class); + DEFAULT_TEXTURE_PROPERTY = new Property( "textures", Constants.DEFAULT_MINECRAFT_JAVA_SKIN_TEXTURE, @@ -116,7 +116,7 @@ public final class VelocityListener { private AttributeKey kickMessageAttribute; @Inject - private HttpClient httpClient; + private MojangUtils mojangUtils; @Subscribe(order = PostOrder.EARLY) public void onPreLogin(PreLoginEvent event) { @@ -159,26 +159,29 @@ public void onGameProfileRequest(GameProfileRequestEvent event) { if (player != null) { playerCache.invalidate(event.getConnection()); - GameProfile profile = new GameProfile( - player.getCorrectUniqueId(), - player.getCorrectUsername(), - player.isLinked() ? Collections.emptyList() : List.of(DEFAULT_TEXTURE_PROPERTY) // Otherwise game server will try to fetch the skin from Mojang - ); + List properties = new ArrayList<>(); if (player.isLinked()) { - // Do texture lookup to session server + // Floodgate players are seen as offline mode players, meaning we have to look up + // the linked player's textures ourselves try { - SkinData skin = MojangUtils.getSkinCached(httpClient, - player.getJavaUniqueId()); - - profile.addProperty(new Property("textures", skin.value(), skin.signature())); - } catch (ExecutionException e) { - logger.error("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", e); - profile.addProperty(DEFAULT_TEXTURE_PROPERTY); + SkinData skin = mojangUtils.skinFor(player.getJavaUniqueId()); + properties.add(new Property("textures", skin.value(), skin.signature())); + } catch (ExecutionException exception) { + logger.debug("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", exception); } } - event.setGameProfile(profile); + // either the player isn't linked or it failed to look up the skin + if (properties.isEmpty()) { + properties.add(DEFAULT_TEXTURE_PROPERTY); + } + + event.setGameProfile(new GameProfile( + player.getCorrectUniqueId(), + player.getCorrectUsername(), + properties + )); } } From be2a94d579ca041aa48cef44ab30f41ef936ce1b Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 18 May 2024 14:49:21 +0200 Subject: [PATCH 10/11] Don't block Velocity event threads, made the Bungee variant work --- .../floodgate/listener/BungeeListener.java | 47 +++++++++-------- .../pluginmessage/BungeeSkinApplier.java | 2 - .../geysermc/floodgate/util/MojangUtils.java | 6 +-- .../floodgate/listener/VelocityListener.java | 50 +++++++++---------- 4 files changed, 49 insertions(+), 56 deletions(-) diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 0de97418..1267f7a8 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -39,6 +39,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; @@ -46,7 +47,6 @@ import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinDataImpl; import org.geysermc.floodgate.util.LanguageManager; @@ -67,7 +67,7 @@ public final class BungeeListener implements Listener { checkNotNull(PLAYER_NAME, "Initial name field cannot be null"); } - @Inject private ProxyFloodgateConfig config; + @Inject private Plugin plugin; @Inject private ProxyFloodgateApi api; @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; @@ -130,29 +130,28 @@ public void onLogin(LoginEvent event) { @EventHandler(priority = EventPriority.LOWEST) public void onPostLogin(PostLoginEvent event) { - if (!config.isSendFloodgateData()) { - FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); - - if (player == null) { - return; - } - - // Floodgate players are seen as offline mode players, meaning we have to look up - // the linked player's textures ourselves - - if (!player.isLinked()) { - skinApplier.applySkin(player, SkinDataImpl.DEFAULT_SKIN); - return; - } - - mojangUtils.asyncSkinFor(player.getJavaUniqueId()) - .thenAccept(skin -> skinApplier.applySkin(player, skin)) - .exceptionally(exception -> { - logger.debug("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", exception); - skinApplier.applySkin(player, SkinDataImpl.DEFAULT_SKIN); - return null; - }); + FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); + + // Skin look up would result in it failing, apply a default skin + if (!player.isLinked()) { + skinApplier.applySkin(player, 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(player.getJavaUniqueId()) + .exceptionally(exception -> { + logger.debug("Unexpected skin fetch error for " + player.getJavaUniqueId(), exception); + return SkinDataImpl.DEFAULT_SKIN; + }) + .thenAccept(skin -> { + skinApplier.applySkin(player, skin); + event.completeIntent(plugin); + }); } @EventHandler(priority = EventPriority.HIGHEST) diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java index ce6105ec..fa2aaad2 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java @@ -92,8 +92,6 @@ public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinDat SkinData currentSkin = currentSkin(properties); SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData); - event.setCancelled(floodgatePlayer.isLinked()); - eventBus.fire(event); if (event.isCancelled()) { diff --git a/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java index 73845cc5..423ff44b 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/MojangUtils.java @@ -55,11 +55,7 @@ public class MojangUtils { @Named("commonPool") private ExecutorService commonPool; - public @NonNull SkinData skinFor(UUID playerId) throws ExecutionException { - return SKIN_CACHE.get(playerId, () -> fetchSkinFor(playerId)); - } - - public CompletableFuture<@NonNull SkinData> asyncSkinFor(UUID playerId) { + public CompletableFuture<@NonNull SkinData> skinFor(UUID playerId) { return CompletableFuture.supplyAsync(() -> { try { return SKIN_CACHE.get(playerId, () -> fetchSkinFor(playerId)); diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index a9369916..fcf2fd52 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -36,6 +36,7 @@ import com.google.common.cache.CacheBuilder; import com.google.inject.Inject; import com.google.inject.name.Named; +import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.connection.DisconnectEvent; @@ -48,17 +49,15 @@ import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; -import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.ProxyFloodgateApi; -import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.skin.SkinDataImpl; import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.MojangUtils; @@ -154,34 +153,35 @@ public void onPreLogin(PreLoginEvent event) { } @Subscribe(order = PostOrder.EARLY) - public void onGameProfileRequest(GameProfileRequestEvent event) { + public void onGameProfileRequest(GameProfileRequestEvent event, Continuation continuation) { FloodgatePlayer player = playerCache.getIfPresent(event.getConnection()); if (player != null) { playerCache.invalidate(event.getConnection()); - List properties = new ArrayList<>(); - - if (player.isLinked()) { - // Floodgate players are seen as offline mode players, meaning we have to look up - // the linked player's textures ourselves - try { - SkinData skin = mojangUtils.skinFor(player.getJavaUniqueId()); - properties.add(new Property("textures", skin.value(), skin.signature())); - } catch (ExecutionException exception) { - logger.debug("Failed to get skin for player " + player.getJavaUniqueId() + ", applying default.", exception); - } + if (!player.isLinked()) { + event.setGameProfile(new GameProfile( + player.getCorrectUniqueId(), + player.getCorrectUsername(), + List.of(DEFAULT_TEXTURE_PROPERTY) + )); + return; } - // either the player isn't linked or it failed to look up the skin - if (properties.isEmpty()) { - properties.add(DEFAULT_TEXTURE_PROPERTY); - } - - event.setGameProfile(new GameProfile( - player.getCorrectUniqueId(), - player.getCorrectUsername(), - properties - )); + // Floodgate players are seen as offline mode players, meaning we have to look up + // the linked player's textures ourselves + + mojangUtils.skinFor(player.getJavaUniqueId()) + .exceptionally(exception -> { + logger.debug("Unexpected skin fetch error for " + player.getJavaUniqueId(), exception); + return SkinDataImpl.DEFAULT_SKIN; + }).thenAccept(skin -> { + event.setGameProfile(new GameProfile( + player.getCorrectUniqueId(), + player.getCorrectUsername(), + List.of(new Property("textures", skin.value(), skin.signature())) + )); + continuation.resume(); + }); } } From 94e77634c2386e90b6350c698462afcb7393bf8c Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 18 May 2024 14:58:05 +0200 Subject: [PATCH 11/11] Add some comments --- .../floodgate/listener/BungeeListener.java | 2 +- .../addon/data/SpigotDataHandler.java | 4 +- .../floodgate/listener/VelocityListener.java | 56 ++++++++++--------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index 1267f7a8..f7e4b6fd 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -132,7 +132,7 @@ public void onLogin(LoginEvent event) { public void onPostLogin(PostLoginEvent event) { FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); - // Skin look up would result in it failing, apply a default skin + // Skin look up (on Spigot and friends) would result in it failing, so apply a default skin if (!player.isLinked()) { skinApplier.applySkin(player, SkinDataImpl.DEFAULT_SKIN); return; diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 08f49125..383fa7e9 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -180,7 +180,9 @@ private boolean checkAndHandleLogin(Object packet) throws Exception { ); if (!player.isLinked()) { - // Otherwise game server will try to fetch the skin from Mojang + // 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); } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index fcf2fd52..82c7abcc 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -155,34 +155,36 @@ public void onPreLogin(PreLoginEvent event) { @Subscribe(order = PostOrder.EARLY) public void onGameProfileRequest(GameProfileRequestEvent event, Continuation continuation) { FloodgatePlayer player = playerCache.getIfPresent(event.getConnection()); - if (player != null) { - playerCache.invalidate(event.getConnection()); - - if (!player.isLinked()) { - event.setGameProfile(new GameProfile( - player.getCorrectUniqueId(), - player.getCorrectUsername(), - List.of(DEFAULT_TEXTURE_PROPERTY) - )); - return; - } - - // Floodgate players are seen as offline mode players, meaning we have to look up - // the linked player's textures ourselves - - mojangUtils.skinFor(player.getJavaUniqueId()) - .exceptionally(exception -> { - logger.debug("Unexpected skin fetch error for " + player.getJavaUniqueId(), exception); - return SkinDataImpl.DEFAULT_SKIN; - }).thenAccept(skin -> { - event.setGameProfile(new GameProfile( - player.getCorrectUniqueId(), - player.getCorrectUsername(), - List.of(new Property("textures", skin.value(), skin.signature())) - )); - continuation.resume(); - }); + if (player == null) { + return; } + playerCache.invalidate(event.getConnection()); + + // Skin look up (on Spigot and friends) would result in it failing, so apply a default skin + if (!player.isLinked()) { + event.setGameProfile(new GameProfile( + player.getCorrectUniqueId(), + player.getCorrectUsername(), + List.of(DEFAULT_TEXTURE_PROPERTY) + )); + return; + } + + // Floodgate players are seen as offline mode players, meaning we have to look up + // the linked player's textures ourselves + + mojangUtils.skinFor(player.getJavaUniqueId()) + .exceptionally(exception -> { + logger.debug("Unexpected skin fetch error for " + player.getJavaUniqueId(), exception); + return SkinDataImpl.DEFAULT_SKIN; + }).thenAccept(skin -> { + event.setGameProfile(new GameProfile( + player.getCorrectUniqueId(), + player.getCorrectUsername(), + List.of(new Property("textures", skin.value(), skin.signature())) + )); + continuation.resume(); + }); } @Subscribe(order = PostOrder.LAST)