1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-28 02:59:16 +00:00

Works on 1.21.9

This commit is contained in:
Camotoy
2025-10-01 20:56:19 -04:00
parent 4f0bffe0e5
commit fa1e5fa571
5 changed files with 42 additions and 22 deletions

View File

@@ -151,6 +151,15 @@ public final class ReflectionUtils {
}
}
@Nullable
public static <T> T newInstanceOrThrow(Constructor<T> constructor, Object... parameters) {
try {
return constructor.newInstance(parameters);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@Nullable
public static <T> T newInstance(Constructor<T> constructor, Object... parameters) {
try {

View File

@@ -28,8 +28,6 @@ package org.geysermc.floodgate.addon.data;
import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.util.ReflectionUtils.setValue;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
@@ -181,16 +179,15 @@ public final class SpigotDataHandler extends CommonDataHandler {
}
}
Multimap<String, Property> properties = MultimapBuilder.hashKeys().arrayListValues().build();
Property texturesProperty = null;
if (!player.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)
properties.put("textures", DEFAULT_TEXTURE_PROPERTY);
texturesProperty = DEFAULT_TEXTURE_PROPERTY;
}
GameProfile gameProfile = versionSpecificMethods.createGameProfile(
player.getCorrectUniqueId(), player.getCorrectUsername(), properties);
player.getCorrectUniqueId(), player.getCorrectUsername(), texturesProperty);
// we have to fake the offline player (login) cycle

View File

@@ -99,10 +99,9 @@ public final class SpigotSkinApplier implements SkinApplier {
}
private void replaceSkin(Player player, FloodgatePlayer floodgatePlayer, SkinData skinData) {
Multimap<String, Property> newProperties = MultimapBuilder.hashKeys().arrayListValues().build();
newProperties.put("textures", new Property("textures", skinData.value(), skinData.signature()));
Property skinProperty = new Property("textures", skinData.value(), skinData.signature());
GameProfile profile = versionSpecificMethods.createGameProfile(floodgatePlayer.getCorrectUniqueId(),
floodgatePlayer.getCorrectUsername(), newProperties);
floodgatePlayer.getCorrectUsername(), skinProperty);
Object entityHuman = ReflectionUtils.invoke(player, ClassNames.GET_ENTITY_HUMAN_METHOD);
ReflectionUtils.setValue(entityHuman, ClassNames.GAME_PROFILE_FIELD, profile);
}

View File

@@ -124,7 +124,8 @@ public class ClassNames {
Class<?> entityHumanClass = getClassOrFallback("net.minecraft.world.entity.player.EntityHuman",
"net.minecraft.world.entity.player.Player");
checkNotNull(entityHumanClass, "EntityHuman class");
GAME_PROFILE_FIELD = getField(entityHumanClass, "gameProfile");
// Since 1.21.9: Spigot obfuscates field name
GAME_PROFILE_FIELD = getFieldOfType(entityHumanClass, GameProfile.class);
checkNotNull(GAME_PROFILE_FIELD, "EntityHuman.gameProfile field");
// SpigotInjector

View File

@@ -25,12 +25,13 @@
package org.geysermc.floodgate.util;
import com.google.common.collect.Multimap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.bukkit.entity.Player;
@@ -49,6 +50,7 @@ public final class SpigotVersionSpecificMethods {
private static final Method NEW_GAME_PROFILE_PROPERTIES;
private static final Constructor<GameProfile> RECORD_GAME_PROFILE_CONSTRUCTOR;
private static final Constructor<PropertyMap> IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR;
private static final Method MULTIMAP_FROM_MAP;
static {
GET_SPIGOT = ReflectionUtils.getMethod(Player.class, "spigot");
@@ -61,11 +63,16 @@ public final class SpigotVersionSpecificMethods {
NEW_PROPERTY_VALUE = ReflectionUtils.getMethod(Property.class, "value");
NEW_PROPERTY_SIGNATURE = ReflectionUtils.getMethod(Property.class, "signature");
NEW_GAME_PROFILE_PROPERTIES = ReflectionUtils.getMethod(GameProfile.class, "properties");
RECORD_GAME_PROFILE_CONSTRUCTOR = ReflectionUtils.getConstructor(GameProfile.class, true, UUID.class, String.class, PropertyMap.class);
// TODO have to do this here because if we get constructor using Multimap.class we try to look for one that takes our
// TODO relocated Multimap, which doesn't exist
IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR = (Constructor<PropertyMap>) PropertyMap.class.getConstructors()[0];
NEW_GAME_PROFILE_PROPERTIES = ReflectionUtils.getMethod(
GameProfile.class, "properties");
RECORD_GAME_PROFILE_CONSTRUCTOR = ReflectionUtils.getConstructor(
GameProfile.class, true, UUID.class, String.class, PropertyMap.class);
IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR = (Constructor<PropertyMap>)
PropertyMap.class.getConstructors()[0];
// Avoid relocation for this class.
Class<?> multimaps = ReflectionUtils.getClass(String.join(".", "com",
"google", "common", "collect", "Multimaps"));
MULTIMAP_FROM_MAP = ReflectionUtils.getMethod(multimaps, "forMap", Map.class);
}
private final SpigotPlugin plugin;
@@ -74,15 +81,22 @@ public final class SpigotVersionSpecificMethods {
this.plugin = plugin;
}
public GameProfile createGameProfile(UUID uuid, String name, Multimap<String, Property> properties) {
public GameProfile createGameProfile(UUID uuid, String name, Property texturesProperty) {
if (RECORD_GAME_PROFILE_CONSTRUCTOR != null && IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR != null) {
// TODO this breaks, as the passed properties to the IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR
// TODO is of our relocated Multimap, and not the one PropertyMap uses
return ReflectionUtils.newInstance(RECORD_GAME_PROFILE_CONSTRUCTOR, uuid, name,
ReflectionUtils.newInstance(IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR, properties));
if (texturesProperty != null) {
Map<String, Property> properties = new HashMap<>();
properties.put("textures", texturesProperty);
Object multimap = ReflectionUtils.invoke(null, MULTIMAP_FROM_MAP, properties);
return ReflectionUtils.newInstanceOrThrow(RECORD_GAME_PROFILE_CONSTRUCTOR, uuid,
name,
ReflectionUtils.newInstanceOrThrow(IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR,
multimap));
}
}
GameProfile profile = new GameProfile(uuid, name);
profile.getProperties().putAll(properties);
if (texturesProperty != null) {
profile.getProperties().put("textures", texturesProperty);
}
return profile;
}