Skin Manager
Loading and managing NPC skins
The SkinManager handles loading player skins from various sources including Mojang's API, direct URLs, and local files.
Accessing the skin manager
SkinManager skinManager = FancyNpcsPlugin.get().getSkinManager();Loading skins
By player UUID
Load a skin using a player's UUID:
import de.oliver.fancynpcs.api.skins.SkinData;
import de.oliver.fancynpcs.api.skins.SkinData.SkinVariant;
UUID playerUUID = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5"); // Notch
SkinData skin = skinManager.getByUUID(playerUUID, SkinVariant.AUTO);
if (skin != null) {
npcData.setSkin(skin);
}By player username
Load a skin using a player's username:
SkinData skin = skinManager.getByUsername("Notch", SkinVariant.AUTO);
if (skin != null) {
npcData.setSkin(skin);
}By URL
Load a skin from an image URL:
String skinUrl = "https://textures.minecraft.net/texture/...";
SkinData skin = skinManager.getByURL(skinUrl, SkinVariant.AUTO);
if (skin != null) {
npcData.setSkin(skin);
}By file
Load a skin from the plugins/FancyNPCs/skins/ directory:
// Place your skin PNG in plugins/FancyNPCs/skins/custom_skin.png
SkinData skin = skinManager.getByFile("custom_skin.png", SkinVariant.AUTO);
if (skin != null) {
npcData.setSkin(skin);
}Using a generic identifier
The skin manager can automatically detect the type of identifier:
// Will automatically detect UUID, username, URL, or file
SkinData skin = skinManager.getByIdentifier("Notch", SkinVariant.AUTO);Skin variants
Skins come in two variants:
SLIM- Alex model (3px wide arms)CLASSIC- Steve model (4px wide arms)AUTO- Automatically detect the variant
// Force classic (Steve) variant
SkinData skin = skinManager.getByUsername("Notch", SkinVariant.CLASSIC);
// Force slim (Alex) variant
SkinData skin = skinManager.getByUsername("Notch", SkinVariant.SLIM);
// Auto-detect variant (recommended)
SkinData skin = skinManager.getByUsername("Notch", SkinVariant.AUTO);Asynchronous loading
Skin loading can be slow, especially when fetching from Mojang's API. The skin manager handles this asynchronously:
// This may return null if the skin hasn't loaded yet
SkinData skin = skinManager.getByUsername("Notch", SkinVariant.AUTO);
if (skin == null) {
// Skin is loading asynchronously
// Listen for SkinGeneratedEvent (see below)
} else {
// Skin was cached and returned immediately
npcData.setSkin(skin);
}Listening for skin loaded events
When a skin loads asynchronously, a SkinGeneratedEvent is fired:
import de.oliver.fancynpcs.api.events.SkinGeneratedEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class SkinListener implements Listener {
@EventHandler
public void onSkinGenerated(SkinGeneratedEvent event) {
String identifier = event.getIdentifier();
SkinData skin = event.getSkinData();
// Apply the loaded skin to your NPC
Npc npc = FancyNpcsPlugin.get().getNpcManager().getNpc("my_npc");
if (npc != null) {
npc.getData().setSkin(skin);
npc.updateForAll();
}
}
}Complete example: Loading skin with fallback
import de.oliver.fancynpcs.api.skins.SkinData;
import de.oliver.fancynpcs.api.skins.SkinData.SkinVariant;
import de.oliver.fancynpcs.api.skins.SkinManager;
import de.oliver.fancynpcs.api.events.SkinGeneratedEvent;
public class SkinLoader implements Listener {
private final String npcName;
private final String skinIdentifier;
public SkinLoader(String npcName, String skinIdentifier) {
this.npcName = npcName;
this.skinIdentifier = skinIdentifier;
}
public void loadSkin() {
SkinManager skinManager = FancyNpcsPlugin.get().getSkinManager();
SkinData skin = skinManager.getByIdentifier(skinIdentifier, SkinVariant.AUTO);
if (skin != null) {
// Skin was cached, apply immediately
applySkin(skin);
} else {
// Skin is loading, will be applied in event handler
plugin.getLogger().info("Loading skin asynchronously: " + skinIdentifier);
}
}
@EventHandler
public void onSkinGenerated(SkinGeneratedEvent event) {
// Check if this is the skin we're waiting for
if (!event.getIdentifier().equalsIgnoreCase(skinIdentifier)) {
return;
}
// Apply the loaded skin
applySkin(event.getSkinData());
}
private void applySkin(SkinData skin) {
Npc npc = FancyNpcsPlugin.get().getNpcManager().getNpc(npcName);
if (npc == null) {
plugin.getLogger().warning("NPC not found: " + npcName);
return;
}
npc.getData().setSkin(skin);
npc.updateForAll();
plugin.getLogger().info("Applied skin to NPC: " + npcName);
}
}SkinData class
The SkinData class contains the skin texture and signature:
public class SkinData {
private final String texture; // Base64 encoded texture data
private final String signature; // Signature for validation
// Get texture value
String texture = skinData.getTexture();
// Get signature
String signature = skinData.getSignature();
}Caching
The skin manager automatically caches loaded skins to improve performance:
- Skins are cached in memory during runtime
- Subsequent requests for the same skin return immediately
- No need to manually manage the cache
Mirror skins
You can make an NPC mirror the skin of nearby players:
// Enable skin mirroring
npcData.setMirrorSkin(true);
// The NPC will automatically copy the skin of the nearest playerMirror skin feature requires the NPC to have setMirrorSkin(true) in NpcData. The actual mirroring logic is handled internally by FancyNPCs.
Creating custom skins
From a texture and signature
If you have raw texture and signature data:
String texture = "eyJ0aW1lc3RhbX..."; // Base64 texture
String signature = "Kj3fL..."; // Signature
SkinData customSkin = new SkinData(texture, signature);
npcData.setSkin(customSkin);From a file
- Place your skin PNG file in
plugins/FancyNPCs/skins/ - Load it using the skin manager:
SkinData skin = skinManager.getByFile("my_custom_skin.png", SkinVariant.CLASSIC);Example: Random skins
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class RandomSkinNpc {
private static final List<String> SKIN_NAMES = Arrays.asList(
"Notch",
"jeb_",
"Dinnerbone",
"Grumm"
);
private static final Random RANDOM = new Random();
public void setRandomSkin(Npc npc) {
String randomName = SKIN_NAMES.get(RANDOM.nextInt(SKIN_NAMES.size()));
SkinManager skinManager = FancyNpcsPlugin.get().getSkinManager();
SkinData skin = skinManager.getByUsername(randomName, SkinVariant.AUTO);
if (skin != null) {
npc.getData().setSkin(skin);
npc.updateForAll();
}
}
public void rotateSkins(Npc npc) {
// Rotate through skins every 30 seconds
Bukkit.getScheduler().runTaskTimer(plugin, () -> {
setRandomSkin(npc);
}, 0L, 600L); // 30 seconds = 600 ticks
}
}Error handling
Handle cases where skins fail to load:
public void loadSkinSafely(Npc npc, String identifier) {
try {
SkinData skin = skinManager.getByIdentifier(identifier, SkinVariant.AUTO);
if (skin == null) {
// Skin loading asynchronously or failed
plugin.getLogger().warning("Skin not immediately available: " + identifier);
return;
}
npc.getData().setSkin(skin);
npc.updateForAll();
} catch (Exception e) {
plugin.getLogger().severe("Failed to load skin: " + e.getMessage());
// Set a fallback skin
SkinData fallback = skinManager.getByUsername("MHF_Villager", SkinVariant.AUTO);
if (fallback != null) {
npc.getData().setSkin(fallback);
npc.updateForAll();
}
}
}