Skills/nms/nms-player-profile/SKILL.md
操作 GameProfile 進行 skin 注入,用於 NPC 外觀設定與假玩家實體(Paper NMS + Mojang-mapped)/ Manipulate GameProfile for skin injection used in NPC appearance and fake player entities
npx skillsauth add MrPippi/MPS nms-player-profileInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
nms-player-profile
透過 NMS GameProfile 操作玩家皮膚(texture)屬性,實現 NPC 外觀注入、假玩家實體皮膚設定,以及客製化頭顱 skull 顯示。
| 參數 | 範例 | 說明 |
|------|------|------|
| package_name | com.example.npc | 產出類別所在 package |
| class_name | ProfileBuilder | Profile 建立器類名 |
| fetch_async | true | 是否非同步從 Mojang API 抓取 skin |
ProfileBuilder.java — GameProfile 建立與 skin 注入工具SkinFetcher.java — 非同步從 Mojang API 抓取 skin textureSkullBuilder.java(選)— 設定頭顱 ItemStack skin參見 Skills/paper-nms/PLATFORM.md。關鍵依賴:
dependencies {
paperweight.paperDevBundle('1.21.1-R0.1-SNAPSHOT')
}
ProfileBuilder.javapackage com.example.npc;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.entity.Player;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import net.minecraft.server.level.ServerPlayer;
import java.util.UUID;
@SuppressWarnings("UnstableApiUsage")
public final class ProfileBuilder {
private ProfileBuilder() {}
/**
* 從現有玩家複製 GameProfile(含 skin texture)。
* 用於將真實玩家外觀複製到 NPC。
*/
public static GameProfile copyFrom(Player player) {
ServerPlayer nms = ((CraftPlayer) player).getHandle();
return nms.getGameProfile();
}
/**
* 建立帶有自定義 skin 的 GameProfile。
*
* @param name 顯示名稱(建議 ≤16 字元)
* @param textureValue Base64 編碼的 texture JSON
* @param textureSignature Mojang 簽名(可為 null,但 1.21 online 模式需要)
*/
public static GameProfile withSkin(String name, String textureValue, String textureSignature) {
GameProfile profile = new GameProfile(UUID.randomUUID(), name);
profile.getProperties().put("textures",
new Property("textures", textureValue, textureSignature));
return profile;
}
/**
* 建立無 skin 的空白 GameProfile(外觀為預設 Steve)。
*/
public static GameProfile blank(String name) {
return new GameProfile(UUID.randomUUID(), name);
}
/**
* 從伺服器 user cache 查詢已知玩家的 Profile(含 skin)。
* 只對曾加入過此伺服器的玩家有效。
*/
public static GameProfile fromCache(String playerName) {
var minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();
var profileResult = minecraftServer.getProfileCache()
.get(playerName);
return profileResult.map(com.mojang.authlib.GameProfile.class::cast).orElse(null);
}
}
SkinFetcher.java(非同步從 Mojang API 抓取)package com.example.npc;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.plugin.Plugin;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("UnstableApiUsage")
public final class SkinFetcher {
private SkinFetcher() {}
/**
* 非同步透過 Mojang session server 抓取完整 GameProfile(含 skin)。
* 回傳 CompletableFuture,結果在非同步執行緒產生,
* 使用前請切換回主執行緒。
*/
public static CompletableFuture<GameProfile> fetchByName(Plugin plugin, String playerName) {
return CompletableFuture.supplyAsync(() -> {
try {
var minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();
var sessionService = minecraftServer.getSessionService();
// Step 1: 透過 profileCache 取得 UUID
var profileOpt = minecraftServer.getProfileCache().get(playerName);
if (profileOpt.isEmpty()) return null;
GameProfile profile = profileOpt.get();
// Step 2: 填充 texture 屬性
return sessionService.fetchProfile(profile.getId(), true)
.profile();
} catch (Exception e) {
plugin.getLogger().warning("Failed to fetch skin for " + playerName + ": " + e.getMessage());
return null;
}
});
}
/** 取得 GameProfile 的 texture value(Base64 JSON)。 */
public static String getTextureValue(GameProfile profile) {
var textures = profile.getProperties().get("textures");
if (textures.isEmpty()) return null;
return textures.iterator().next().value();
}
/** 取得 GameProfile 的 texture signature。 */
public static String getTextureSignature(GameProfile profile) {
var textures = profile.getProperties().get("textures");
if (textures.isEmpty()) return null;
return textures.iterator().next().signature();
}
}
SkullBuilder.java(頭顱 ItemStack skin 設定)package com.example.npc;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
@SuppressWarnings("UnstableApiUsage")
public final class SkullBuilder {
private SkullBuilder() {}
/**
* 建立帶有指定 GameProfile skin 的玩家頭顱 ItemStack。
*/
public static org.bukkit.inventory.ItemStack withProfile(GameProfile profile) {
ItemStack nms = new ItemStack(Items.PLAYER_HEAD);
CompoundTag tag = nms.getOrCreateTag();
CompoundTag skullOwner = NbtUtils.writeGameProfile(new CompoundTag(), profile);
tag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(nms);
}
}
src/main/java/com/example/
├── MyNmsPlugin.java
└── npc/
├── ProfileBuilder.java
├── SkinFetcher.java
└── SkullBuilder.java
ProfileBuilder 方法為純資料操作,可在任意執行緒呼叫SkinFetcher.fetchByName() 在 async 執行緒抓取,不可在回呼中直接操作 Bukkit/NMS 世界Skills/_shared/nms-threading.md| 錯誤 | 原因 | 解法 |
|------|------|------|
| skin 不顯示 | texture signature 為 null(offline 模式伺服器) | offline mode 下 signature 可省略,但部分客戶端會拒絕 |
| fetchByName 回傳 null | 玩家從未加入此伺服器 | 改用直接傳入 texture Base64 字串 |
| NPC 皮膚顯示預設 Steve | Profile UUID 未正確設定 | 確保 UUID 非全零,建議使用 UUID.randomUUID() |
| 頭顱 skin 不更新 | 使用 Bukkit ItemMeta 設定(會被 NMS 覆蓋) | 改用 SkullBuilder.withProfile() 的 NMS 方式 |
development
透過 NMS Scoreboard/Objective/Team API 操作 sidebar、tablist 顯示名稱與計分板(Paper NMS + Mojang-mapped)/ Operate sidebar, tablist, and scoreboard via NMS Scoreboard/Objective/Team API
tools
透過 ClientboundLevelParticlesPacket 實現進階 NMS 粒子效果:客戶端專屬、大量粒子、自定義參數(Paper NMS + Mojang-mapped)/ Advanced NMS particle effects via ClientboundLevelParticlesPacket with per-client and bulk support
documentation
直接操作 CompoundTag 讀寫物品、實體、方塊實體的 NBT 資料(Paper NMS + Mojang-mapped)/ Read and write NBT data on items, entities, and block entities via CompoundTag
documentation
操作 Minecraft 1.21 DataComponentType 物品組件系統,讀寫 CustomData、MaxStackSize、Enchantments 等組件(Paper NMS + Mojang-mapped)/ Read and write 1.21 DataComponentType item components including CustomData, MaxStackSize, Enchantments