Skip to content

Instantly share code, notes, and snippets.

@JohnnyJayJay
Last active March 6, 2023 05:39
Show Gist options
  • Save JohnnyJayJay/9519c9b9368af09ffbde7cdab9211ff5 to your computer and use it in GitHub Desktop.
Save JohnnyJayJay/9519c9b9368af09ffbde7cdab9211ff5 to your computer and use it in GitHub Desktop.
code to get custom heads in minecraft in any version
// Comment the line below in if you use compatre
//import com.github.johnnyjayjay.compatre.NmsDependent;
import net.minecraft.server.v1_8_R3.MojangsonParser;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.UUID;
/**
* Class with a utility method to get custom heads as ItemStacks with Spigot.
* The code below can easily be adjusted to work with any version
* (only the craftbukkit/nms package names and perhaps the material need to be changed).
*
* If you want to compile it once and be compatible with any version, you only need to
* find a way around the obfuscated packages. You can either use reflection for this
* (as is common practice), or <a href="https://github.com/johnnyjayjay/compatre">compatre</a>
* (much simpler).
*
* As it stands, the code is compiled against 1.8.8.
*
* @author JohnnyJayJay (https://github.com/johnnyjayjay)
*/
// Comment the line below in if you use compatre
//@NmsDependent
public class CustomHeads {
/* Since version 1.16.1, UUIDs in NBT tags are no longer serialised as strings,
but as an int array of 4 x 32 bits, going from most significant to least significant
bits of the UUID. */
private static final boolean newStorageSystem;
static {
String versionString = Bukkit.getBukkitVersion();
int[] version = Arrays.stream(versionString.substring(0, versionString.indexOf('-')).split("\\."))
.mapToInt(Integer::parseInt)
.toArray();
newStorageSystem = version[0] > 1
|| (version[0] == 1 && version[1] > 16)
|| (version[0] == 1 && version[1] == 16 && version[2] >= 1);
}
/**
* Creates a skull item stack that uses the given base64-encoded texture
*
* @param texture The texture value. Can be found on e.g. https://minecraft-heads.com/custom-heads/
* in the "Value" field.
* @return an ItemStack with this texture.
*/
public static ItemStack create(String texture) {
// for non-legacy api versions, use PLAYER_HEAD or LEGACY_SKULL_ITEM instead of SKULL_ITEM.
ItemStack item = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
/* This code is compiled against version 1.8.8, but works on any version if you use
the correct package name. */
net.minecraft.server.v1_8_R3.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
try {
String nbtString = String.format(
"{SkullOwner:{Id:%s,Properties:{textures:[{Value:\"%s\"}]}}}",
serializeUuid(UUID.randomUUID()), texture
);
NBTTagCompound nbt = MojangsonParser.parse(nbtString);
nmsItem.setTag(nbt);
} catch (Exception e) { // Not catching a more specific exception here because that exception changes across versions
throw new AssertionError("NBT Tag parsing failed - This should never happen.", e);
}
return CraftItemStack.asBukkitCopy(nmsItem);
}
private static String serializeUuid(UUID uuid) {
if (newStorageSystem) {
StringBuilder result = new StringBuilder();
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
return result.append("[I;")
.append(msb >> 32)
.append(',')
.append(msb & Integer.MAX_VALUE)
.append(',')
.append(lsb >> 32)
.append(',')
.append(lsb & Integer.MAX_VALUE)
.append(']')
.toString();
} else {
return '"' + uuid.toString() + '"';
}
}
}
@Amejonah1200
Copy link

Pls add also a Paper (PlayerProfileWrapper) and Spigot (PlayerProfile) variant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment