Skip to content

Commit

Permalink
Merge pull request #361 from Dreeam-qwq/master
Browse files Browse the repository at this point in the history
Update XSkull to fix skull skin on 1.20.2
  • Loading branch information
Bkm016 authored Oct 21, 2023
2 parents 4e6c711 + f5870dc commit 9da35a7
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 10 deletions.
2 changes: 2 additions & 0 deletions platform/platform-bukkit/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies {
compileOnly(project(":module:module-configuration"))
compileOnly("org.tabooproject.reflex:reflex:1.0.19")
compileOnly("org.tabooproject.reflex:analyser:1.0.19")

compileOnly(fileTree("libs"))
}

tasks {
Expand Down
Binary file added platform/platform-bukkit/libs/authlib-5.0.47.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import java.lang.invoke.MethodType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Block;
Expand Down Expand Up @@ -55,15 +56,18 @@
* <ul>
* <li>https://minecraft-heads.com/</li>
* </ul>
* <p>
* The basic premise behind this API is that the final skull data is contained in a {@link GameProfile}
* either by ID, name or encoded textures URL property.
*
* @author Crypto Morin
* @version 3.1.1
* @version 4.0.3
* @see XMaterial
*/
public class XSkull {
protected static final MethodHandle
CRAFT_META_SKULL_PROFILE_GETTER, CRAFT_META_SKULL_PROFILE_SETTER,
CRAFT_META_SKULL_BLOCK_SETTER;
CRAFT_META_SKULL_BLOCK_SETTER, PROPERTY_GETVALUE;

/**
* Some people use this without quotes surrounding the keys, not sure what that'd work.
Expand All @@ -84,6 +88,17 @@ public class XSkull {
*/
private static final Pattern MOJANG_SHA256_APPROX = Pattern.compile("[0-9a-z]{60,70}");

/**
* In v1.20.2 there were some changes to the mojang API.
*/
private static final boolean NULLABILITY_RECORD_UPDATE = ReflectionUtils.VERSION.equals("v1_20_R2");

/**
* Does using a random UUID have any advantage?
*/
private static final UUID GAME_PROFILE_EMPTY_UUID = NULLABILITY_RECORD_UPDATE ? new UUID(0, 0) : null;
private static final String GAME_PROFILE_EMPTY_NAME = NULLABILITY_RECORD_UPDATE ? "" : null;

/**
* The value after this URL is probably an SHA-252 value that Mojang uses to unique identify player skins.
* <br>
Expand All @@ -94,7 +109,7 @@ public class XSkull {

static {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle profileSetter = null, profileGetter = null, blockSetter = null;
MethodHandle profileSetter = null, profileGetter = null, blockSetter = null, propGetval = null;

try {
Class<?> CraftMetaSkull = ReflectionUtils.getCraftClass("inventory.CraftMetaSkull");
Expand All @@ -110,7 +125,7 @@ public class XSkull {
} catch (NoSuchMethodException e) {
profileSetter = lookup.unreflectSetter(profile);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
} catch (Throwable e) {
e.printStackTrace();
}

Expand All @@ -124,6 +139,16 @@ public class XSkull {
e.printStackTrace();
}

if (!NULLABILITY_RECORD_UPDATE) {
try {
//noinspection JavaLangInvokeHandleSignature
propGetval = lookup.findVirtual(Property.class, "getValue", MethodType.methodType(String.class));
} catch (Throwable ex) {
ex.printStackTrace();
}
}

PROPERTY_GETVALUE = propGetval;
CRAFT_META_SKULL_PROFILE_SETTER = profileSetter;
CRAFT_META_SKULL_PROFILE_GETTER = profileGetter;
CRAFT_META_SKULL_BLOCK_SETTER = blockSetter;
Expand Down Expand Up @@ -192,7 +217,9 @@ protected static SkullMeta setSkullBase64(@NotNull SkullMeta head, @NotNull Stri

@NotNull
public static GameProfile profileFromBase64(String value) {
GameProfile profile = new GameProfile(UUID.randomUUID(), null);
// Use an empty string instead of null for the name parameter because it's now null-checked since 1.20.2.
// It doesn't seem to affect functionality.
GameProfile profile = new GameProfile(GAME_PROFILE_EMPTY_UUID, GAME_PROFILE_EMPTY_NAME);
profile.getProperties().put("textures", new Property("textures", value));
return profile;
}
Expand All @@ -206,8 +233,8 @@ public static GameProfile profileFromPlayer(OfflinePlayer player) {
public static GameProfile detectProfileFromString(String identifier) {
// @formatter:off sometimes programming is just art that a machine can't understand :)
switch (detectSkullValueType(identifier)) {
case UUID: return new GameProfile(UUID.fromString( identifier), null);
case NAME: return new GameProfile(null, identifier);
case UUID: return new GameProfile(UUID.fromString( identifier), GAME_PROFILE_EMPTY_NAME);
case NAME: return new GameProfile(GAME_PROFILE_EMPTY_UUID, identifier);
case BASE64: return profileFromBase64( identifier);
case TEXTURE_URL: return profileFromBase64(encodeTexturesURL( identifier));
case TEXTURE_HASH: return profileFromBase64(encodeTexturesURL(TEXTURES + identifier));
Expand Down Expand Up @@ -284,14 +311,32 @@ public static ItemBuilder.SkullTexture getSkinValue(@NotNull ItemMeta skull) {
}
if (profile != null && !profile.getProperties().get("textures").isEmpty()) {
for (Property property : profile.getProperties().get("textures")) {
if (!property.getValue().isEmpty()) {
return new ItemBuilder.SkullTexture(property.getValue(), profile.getId());
String value = getPropertyValue(property);
if (!value.isEmpty()) {
return new ItemBuilder.SkullTexture(value, profile.getId());
}
}
}
return null;
}

/**
* They changed {@link Property} to a Java record in 1.20.2
*
* @since 4.0.1
*/
private static String getPropertyValue(Property property) {
if (NULLABILITY_RECORD_UPDATE) {
return property.value();
} else {
try {
return (String) PROPERTY_GETVALUE.invoke(property);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}

/**
* https://help.minecraft.net/hc/en-us/articles/360034636712
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ open class ItemBuilder {
itemMeta.owner = skullOwner
}
if (skullTexture != null) {
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, null).also {
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, "null").also {
it.properties.put("textures", Property("textures", skullTexture!!.textures))
})
}
Expand Down

0 comments on commit 9da35a7

Please sign in to comment.