Skip to content

Commit 9da35a7

Browse files
authored
Merge pull request #361 from Dreeam-qwq/master
Update XSkull to fix skull skin on 1.20.2
2 parents 4e6c711 + f5870dc commit 9da35a7

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed

platform/platform-bukkit/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ dependencies {
2525
compileOnly(project(":module:module-configuration"))
2626
compileOnly("org.tabooproject.reflex:reflex:1.0.19")
2727
compileOnly("org.tabooproject.reflex:analyser:1.0.19")
28+
29+
compileOnly(fileTree("libs"))
2830
}
2931

3032
tasks {
Binary file not shown.

platform/platform-bukkit/src/main/java/taboolib/library/xseries/XSkull.java

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.common.collect.Lists;
2525
import com.mojang.authlib.GameProfile;
2626
import com.mojang.authlib.properties.Property;
27+
import java.lang.invoke.MethodType;
2728
import org.bukkit.Bukkit;
2829
import org.bukkit.OfflinePlayer;
2930
import org.bukkit.block.Block;
@@ -55,15 +56,18 @@
5556
* <ul>
5657
* <li>https://minecraft-heads.com/</li>
5758
* </ul>
59+
* <p>
60+
* The basic premise behind this API is that the final skull data is contained in a {@link GameProfile}
61+
* either by ID, name or encoded textures URL property.
5862
*
5963
* @author Crypto Morin
60-
* @version 3.1.1
64+
* @version 4.0.3
6165
* @see XMaterial
6266
*/
6367
public class XSkull {
6468
protected static final MethodHandle
6569
CRAFT_META_SKULL_PROFILE_GETTER, CRAFT_META_SKULL_PROFILE_SETTER,
66-
CRAFT_META_SKULL_BLOCK_SETTER;
70+
CRAFT_META_SKULL_BLOCK_SETTER, PROPERTY_GETVALUE;
6771

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

91+
/**
92+
* In v1.20.2 there were some changes to the mojang API.
93+
*/
94+
private static final boolean NULLABILITY_RECORD_UPDATE = ReflectionUtils.VERSION.equals("v1_20_R2");
95+
96+
/**
97+
* Does using a random UUID have any advantage?
98+
*/
99+
private static final UUID GAME_PROFILE_EMPTY_UUID = NULLABILITY_RECORD_UPDATE ? new UUID(0, 0) : null;
100+
private static final String GAME_PROFILE_EMPTY_NAME = NULLABILITY_RECORD_UPDATE ? "" : null;
101+
87102
/**
88103
* The value after this URL is probably an SHA-252 value that Mojang uses to unique identify player skins.
89104
* <br>
@@ -94,7 +109,7 @@ public class XSkull {
94109

95110
static {
96111
MethodHandles.Lookup lookup = MethodHandles.lookup();
97-
MethodHandle profileSetter = null, profileGetter = null, blockSetter = null;
112+
MethodHandle profileSetter = null, profileGetter = null, blockSetter = null, propGetval = null;
98113

99114
try {
100115
Class<?> CraftMetaSkull = ReflectionUtils.getCraftClass("inventory.CraftMetaSkull");
@@ -110,7 +125,7 @@ public class XSkull {
110125
} catch (NoSuchMethodException e) {
111126
profileSetter = lookup.unreflectSetter(profile);
112127
}
113-
} catch (NoSuchFieldException | IllegalAccessException e) {
128+
} catch (Throwable e) {
114129
e.printStackTrace();
115130
}
116131

@@ -124,6 +139,16 @@ public class XSkull {
124139
e.printStackTrace();
125140
}
126141

142+
if (!NULLABILITY_RECORD_UPDATE) {
143+
try {
144+
//noinspection JavaLangInvokeHandleSignature
145+
propGetval = lookup.findVirtual(Property.class, "getValue", MethodType.methodType(String.class));
146+
} catch (Throwable ex) {
147+
ex.printStackTrace();
148+
}
149+
}
150+
151+
PROPERTY_GETVALUE = propGetval;
127152
CRAFT_META_SKULL_PROFILE_SETTER = profileSetter;
128153
CRAFT_META_SKULL_PROFILE_GETTER = profileGetter;
129154
CRAFT_META_SKULL_BLOCK_SETTER = blockSetter;
@@ -192,7 +217,9 @@ protected static SkullMeta setSkullBase64(@NotNull SkullMeta head, @NotNull Stri
192217

193218
@NotNull
194219
public static GameProfile profileFromBase64(String value) {
195-
GameProfile profile = new GameProfile(UUID.randomUUID(), null);
220+
// Use an empty string instead of null for the name parameter because it's now null-checked since 1.20.2.
221+
// It doesn't seem to affect functionality.
222+
GameProfile profile = new GameProfile(GAME_PROFILE_EMPTY_UUID, GAME_PROFILE_EMPTY_NAME);
196223
profile.getProperties().put("textures", new Property("textures", value));
197224
return profile;
198225
}
@@ -206,8 +233,8 @@ public static GameProfile profileFromPlayer(OfflinePlayer player) {
206233
public static GameProfile detectProfileFromString(String identifier) {
207234
// @formatter:off sometimes programming is just art that a machine can't understand :)
208235
switch (detectSkullValueType(identifier)) {
209-
case UUID: return new GameProfile(UUID.fromString( identifier), null);
210-
case NAME: return new GameProfile(null, identifier);
236+
case UUID: return new GameProfile(UUID.fromString( identifier), GAME_PROFILE_EMPTY_NAME);
237+
case NAME: return new GameProfile(GAME_PROFILE_EMPTY_UUID, identifier);
211238
case BASE64: return profileFromBase64( identifier);
212239
case TEXTURE_URL: return profileFromBase64(encodeTexturesURL( identifier));
213240
case TEXTURE_HASH: return profileFromBase64(encodeTexturesURL(TEXTURES + identifier));
@@ -284,14 +311,32 @@ public static ItemBuilder.SkullTexture getSkinValue(@NotNull ItemMeta skull) {
284311
}
285312
if (profile != null && !profile.getProperties().get("textures").isEmpty()) {
286313
for (Property property : profile.getProperties().get("textures")) {
287-
if (!property.getValue().isEmpty()) {
288-
return new ItemBuilder.SkullTexture(property.getValue(), profile.getId());
314+
String value = getPropertyValue(property);
315+
if (!value.isEmpty()) {
316+
return new ItemBuilder.SkullTexture(value, profile.getId());
289317
}
290318
}
291319
}
292320
return null;
293321
}
294322

323+
/**
324+
* They changed {@link Property} to a Java record in 1.20.2
325+
*
326+
* @since 4.0.1
327+
*/
328+
private static String getPropertyValue(Property property) {
329+
if (NULLABILITY_RECORD_UPDATE) {
330+
return property.value();
331+
} else {
332+
try {
333+
return (String) PROPERTY_GETVALUE.invoke(property);
334+
} catch (Throwable e) {
335+
throw new RuntimeException(e);
336+
}
337+
}
338+
}
339+
295340
/**
296341
* https://help.minecraft.net/hc/en-us/articles/360034636712
297342
*

platform/platform-bukkit/src/main/kotlin/taboolib/platform/util/ItemBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ open class ItemBuilder {
257257
itemMeta.owner = skullOwner
258258
}
259259
if (skullTexture != null) {
260-
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, null).also {
260+
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, "null").also {
261261
it.properties.put("textures", Property("textures", skullTexture!!.textures))
262262
})
263263
}

0 commit comments

Comments
 (0)