27
27
import org .bukkit .entity .Entity ;
28
28
import org .bukkit .inventory .ItemStack ;
29
29
import org .bukkit .inventory .meta .SkullMeta ;
30
+ import org .bukkit .profile .PlayerProfile ;
31
+ import org .bukkit .profile .PlayerTextures ;
30
32
33
+ import java .net .MalformedURLException ;
34
+ import java .net .URL ;
31
35
import java .util .HashMap ;
32
36
import java .util .UUID ;
37
+ import java .util .Base64 ;
38
+ import org .json .simple .JSONObject ;
39
+ import org .json .simple .parser .JSONParser ;
40
+ import org .json .simple .parser .ParseException ;
33
41
34
42
public class BlockUtil {
35
43
static final int MAX_USERNAME_LENGTH = 16 ;
@@ -117,19 +125,54 @@ public static void setHeadType(String psType, Block b) {
117
125
}
118
126
}
119
127
128
+ public static PlayerProfile getProfile (String uuid , String base64 ) {
129
+ String name = uuid .substring (0 , 16 );
130
+ PlayerProfile profile = Bukkit .getServer ().createPlayerProfile (UUID .fromString (uuid ), name );
131
+ PlayerTextures textures = profile .getTextures ();
132
+
133
+ // decode base64 to URL
134
+ byte [] decodedBytes = Base64 .getDecoder ().decode (base64 );
135
+ String decodedString = new String (decodedBytes );
136
+
137
+ // read decoded string as JSON object
138
+ // sample: {"textures":{"SKIN":{"url":"http://textures.minecraft.net/texture/..."}}}
139
+ String url = "" ;
140
+ try {
141
+ JSONParser parser = new JSONParser ();
142
+ JSONObject object = (JSONObject ) parser .parse (decodedString );
143
+ JSONObject jsonTextures = (JSONObject ) object .get ("textures" );
144
+ JSONObject jsonSkin = (JSONObject ) jsonTextures .get ("SKIN" );
145
+ url = (String ) jsonSkin .get ("url" );
146
+ } catch (ParseException exception ) {
147
+ throw new RuntimeException ("Invalid JSON retrieved from base64 " + decodedString , exception );
148
+ }
149
+
150
+ URL urlObject ;
151
+ try {
152
+ urlObject = new URL (url );
153
+ } catch (MalformedURLException exception ) {
154
+ throw new RuntimeException ("Invalid decoded URL from head data: " + url , exception );
155
+ }
156
+
157
+ textures .setSkin (urlObject );
158
+ profile .setTextures (textures );
159
+ return profile ;
160
+ }
161
+
120
162
public static ItemStack setHeadType (String psType , ItemStack item ) {
121
163
String name = psType .split (":" )[1 ];
122
164
if (name .length () > MAX_USERNAME_LENGTH ) { // base 64 head
123
165
String uuid = name ;
124
166
125
- // if 1.16 or above, use new uuid format
126
- if (Integer .parseInt (MiscUtil .getVersionString ().split ("\\ ." )[1 ]) >= 16 || !MiscUtil .getVersionString ().split ("\\ ." )[0 ].equals ("1" )) {
127
- uuid = MiscUtil .getUniqueIdIntArray (UUID .fromString (uuid ));
128
- } else { // quotes are needed for pre 1.16 uuids
129
- uuid = "\" " + uuid + "\" " ;
130
- }
167
+ // decode base64 to URL
168
+ String base64 = uuidToBase64Head .get (name );
169
+ PlayerProfile profile = getProfile (uuid , base64 );
170
+
171
+ SkullMeta meta = (SkullMeta ) item .getItemMeta ();
172
+ meta .setOwnerProfile (profile );
173
+ item .setItemMeta (meta );
131
174
132
- return Bukkit . getUnsafe (). modifyItemStack ( item , "{SkullOwner:{Name: \" " + name + " \" ,Id:" + uuid + ",Properties:{textures:[{Value: \" " + uuidToBase64Head . get ( name ) + " \" }]}}}" ) ;
175
+ return item ;
133
176
} else { // normal name head
134
177
SkullMeta sm = (SkullMeta ) item .getItemMeta ();
135
178
sm .setOwningPlayer (Bukkit .getOfflinePlayer (name ));
@@ -138,40 +181,13 @@ public static ItemStack setHeadType(String psType, ItemStack item) {
138
181
}
139
182
}
140
183
141
- // Note: this code is really weird
142
184
private static void blockWithBase64 (Block block , String uuid ) {
143
- String base64 = uuidToBase64Head .get (uuid ), args ;
144
-
145
- // if 1.16 or above, use new uuid format
146
- if (Integer .parseInt (MiscUtil .getVersionString ().split ("\\ ." )[1 ]) >= 16 || !MiscUtil .getVersionString ().split ("\\ ." )[0 ].equals ("1" )) {
147
- uuid = MiscUtil .getUniqueIdIntArray (UUID .fromString (uuid ));
148
- args = String .format (
149
- "%d %d %d %s" ,
150
- block .getX (),
151
- block .getY (),
152
- block .getZ (),
153
- "{SkullOwner:{Name:\" " + uuid + "\" ,Id:" + uuid + ",Properties:{textures:[{Value:\" " + base64 + "\" }]}}}"
154
- );
155
- } else { // tag was different pre 1.16
156
- args = String .format (
157
- "%d %d %d %s" ,
158
- block .getX (),
159
- block .getY (),
160
- block .getZ (),
161
- "{Owner:{Name:\" " + uuid + "\" ,Id:\" " + uuid + "\" ,Properties:{textures:[{Value:\" " + base64 + "\" }]}}}"
162
- );
163
- }
164
-
165
- // fake entity to run command at its location
166
- Entity e = block .getWorld ().spawn (new Location (block .getWorld (), 0 , 0 , 0 ), ArmorStand .class , ent -> {
167
- ent .setCustomName ("mrpig" );
168
- ent .setInvulnerable (true );
169
- ent .setVisible (false );
170
- });
185
+ String base64 = uuidToBase64Head .get (uuid );
186
+ PlayerProfile profile = getProfile (uuid , base64 );
171
187
172
- // run data command to change block using the pig's world
173
- Bukkit . dispatchCommand ( Bukkit . getConsoleSender (), "execute at @e[type=armor_stand,nbt={CustomName:'{ \" extra \" :[{ \" text \" : \" " + e . getName () + " \" }], \" text \" : \" \" }'}] run data merge block " + args );
174
- e . remove ( );
188
+ Skull skull = ( Skull ) block . getState ();
189
+ skull . setOwnerProfile ( profile );
190
+ skull . update ( false );
175
191
}
176
192
177
193
public static boolean isBase64PSHead (String type ) {
0 commit comments