Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -135,6 +136,39 @@ public static Class<?> getClassOrFallback(String className, String fallbackClass
return clazz;
}

public static Class<?> getClassOrFallback(String... classNames) {
for (String className : classNames) {
Class<?> clazz = getClassSilently(className);
if (clazz != null) {
if (Constants.DEBUG_MODE) {
System.out.println("Found class: " + clazz.getName() + " for choices: " +
Arrays.toString(classNames));
}
return clazz;
}
}

throw new IllegalStateException("Could not find class between these choices: " +
Arrays.toString(classNames));
}

@Nullable
public static Class<?> getClassOrFallbackSilently(String... classNames) {
for (String className : classNames) {
Class<?> clazz = getClassSilently(className);
if (clazz != null) {
return clazz;
}
}
return null;
}

@Nullable
@SuppressWarnings("unchecked")
public static <T> Class<T> getCastedClassOrFallback(String className, String fallbackClassName) {
return (Class<T>) getClassOrFallback(className, fallbackClassName);
}

@Nullable
public static <T> Constructor<T> getConstructor(Class<T> clazz, boolean declared, Class<?>... parameters) {
try {
Expand All @@ -151,6 +185,15 @@ public static <T> Constructor<T> getConstructor(Class<T> clazz, boolean declared
}
}

@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 {
Expand Down Expand Up @@ -201,6 +244,28 @@ public static Field getField(Class<?> clazz, String fieldName) {
return getField(clazz, fieldName, false);
}

/**
* Get a field from a class, it doesn't matter if the field is public or not. This method will
* first try to get a declared field and if that failed it'll try to get a public field.
*
* @param clazz the class to get the field from
* @param fieldName the name of the field
* @param fallbackFieldName the fallback incase fieldName doesn't exist
* @return the field if found from the name or fallback, otherwise null
*/
@Nullable
public static Field getField(Class<?> clazz, String fieldName, String fallbackFieldName) {
Field field = getField(clazz, fieldName, true);
if (field != null) {
return field;
}
field = getField(clazz, fieldName, false);
if (field != null) {
return field;
}
return getField(clazz, fallbackFieldName);
}

/**
* Get a field from a class without having to provide a field name.
*
Expand Down Expand Up @@ -409,6 +474,29 @@ public static Method getMethod(Class<?> clazz, String methodName, Class<?>... ar
return getMethod(clazz, methodName, false, arguments);
}

/**
* Get a method from a class, it doesn't matter if the method is public or not. This method will
* first try to get a declared method and if that fails it'll try to get a public method.
*
* @param clazz the class to get the method from
* @param methodName the name of the method to find
* @param fallbackName the fallback instead methodName does not exist
* @param arguments the classes of the method arguments
* @return the requested method if it has been found, otherwise null
*/
@Nullable
public static Method getMethod(Class<?> clazz, String methodName, String fallbackName, Class<?>... arguments) {
Method method = getMethod(clazz, methodName, true, arguments);
if (method != null) {
return method;
}
method = getMethod(clazz, methodName, false, arguments);
if (method != null) {
return method;
}
return getMethod(clazz, fallbackName, arguments);
}

/**
* Get a method from a class, it doesn't matter if the method is public or not. This method will
* first try to get a declared method and if that fails it'll try to get a public method.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.parallel=true

version=2.2.4-SNAPSHOT
version=2.2.5-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;

public final class SpigotDataAddon implements InjectorAddon {
@Inject private FloodgateHandshakeHandler handshakeHandler;
Expand All @@ -54,12 +55,15 @@ public final class SpigotDataAddon implements InjectorAddon {
@Named("playerAttribute")
private AttributeKey<FloodgatePlayer> playerAttribute;

@Inject
private SpigotVersionSpecificMethods versionSpecificMethods;

@Override
public void onInject(Channel channel, boolean toServer) {
// we have to add the packet blocker in the data handler, otherwise ProtocolSupport breaks
channel.pipeline().addBefore(
packetHandlerName, "floodgate_data_handler",
new SpigotDataHandler(handshakeHandler, config, kickMessageAttribute)
new SpigotDataHandler(handshakeHandler, config, kickMessageAttribute, versionSpecificMethods)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.geysermc.floodgate.util.ClassNames;
import org.geysermc.floodgate.util.Constants;
import org.geysermc.floodgate.util.ProxyUtils;
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;

public final class SpigotDataHandler extends CommonDataHandler {
private static final Property DEFAULT_TEXTURE_PROPERTY = new Property(
Expand All @@ -49,15 +50,18 @@ public final class SpigotDataHandler extends CommonDataHandler {
Constants.DEFAULT_MINECRAFT_JAVA_SKIN_SIGNATURE
);

private final SpigotVersionSpecificMethods versionSpecificMethods;
private Object networkManager;
private FloodgatePlayer player;
private boolean proxyData;

public SpigotDataHandler(
FloodgateHandshakeHandler handshakeHandler,
FloodgateConfig config,
AttributeKey<String> kickMessageAttribute) {
AttributeKey<String> kickMessageAttribute,
SpigotVersionSpecificMethods versionSpecificMethods) {
super(handshakeHandler, config, kickMessageAttribute, new PacketBlocker());
this.versionSpecificMethods = versionSpecificMethods;
}

@Override
Expand Down Expand Up @@ -175,16 +179,15 @@ private boolean checkAndHandleLogin(Object packet) throws Exception {
}
}

GameProfile gameProfile = new GameProfile(
player.getCorrectUniqueId(), player.getCorrectUsername()
);

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)
gameProfile.getProperties().put("textures", DEFAULT_TEXTURE_PROPERTY);
texturesProperty = DEFAULT_TEXTURE_PROPERTY;
}
GameProfile gameProfile = versionSpecificMethods.createGameProfile(
player.getCorrectUniqueId(), player.getCorrectUsername(), texturesProperty);

// we have to fake the offline player (login) cycle

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ private void applySkin0(FloodgatePlayer floodgatePlayer, SkinData skinData, bool

// Need to be careful here - getProperties() returns an authlib PropertyMap, which extends
// MultiMap from Guava. Floodgate relocates Guava.
PropertyMap properties = profile.getProperties();

SkinData currentSkin = versionSpecificMethods.currentSkin(properties);
SkinData currentSkin = versionSpecificMethods.currentSkin(profile);

SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
event.setCancelled(floodgatePlayer.isLinked());
Expand All @@ -88,7 +86,12 @@ private void applySkin0(FloodgatePlayer floodgatePlayer, SkinData skinData, bool
return;
}

replaceSkin(properties, event.newSkin());
if (ClassNames.GAME_PROFILE_FIELD != null) {
replaceSkin(player, profile, event.newSkin());
} else {
// We're on a version with mutable GameProfiles
replaceSkinOld(profile.getProperties(), event.newSkin());
}

versionSpecificMethods.maybeSchedule(() -> {
for (Player p : Bukkit.getOnlinePlayers()) {
Expand All @@ -99,7 +102,14 @@ private void applySkin0(FloodgatePlayer floodgatePlayer, SkinData skinData, bool
});
}

private void replaceSkin(PropertyMap properties, SkinData skinData) {
private void replaceSkin(Player player, GameProfile oldProfile, SkinData skinData) {
Property skinProperty = new Property("textures", skinData.value(), skinData.signature());
GameProfile profile = versionSpecificMethods.createGameProfile(oldProfile, skinProperty);
Object entityHuman = ReflectionUtils.invoke(player, ClassNames.GET_ENTITY_HUMAN_METHOD);
ReflectionUtils.setValue(entityHuman, ClassNames.GAME_PROFILE_FIELD, profile);
}

private void replaceSkinOld(PropertyMap properties, SkinData skinData) {
properties.removeAll("textures");
Property property = new Property("textures", skinData.value(), skinData.signature());
properties.put("textures", property);
Expand Down
Loading
Loading