Build, refactor, and debug custom items for CustomNPCs Unofficial 1.21.1 in this repository. Use when creating scripted items that need a display name, lore for user-facing description, metadata in CUSTOM_DATA for technical fields, or runtime item workflows coordinated by tempdata-based controllers.
Use this skill for custom item creation in this repository.
This project targets CustomNPCs Unofficial on Minecraft 1.21.1, where item logic should be built around MCItemStack and data components, not older direct NBT helper assumptions.
For runtime controller architecture around item workflows, also read:
docs/customnpcs_tempdata_object_notes.mdTreat every custom item as three layers:
DataComponents.CUSTOM_NAMEDataComponents.LOREDataComponents.CUSTOM_DATADo not mix technical metadata into lore unless the user explicitly wants visible debug info.
Default flow:
BuiltInRegistries.ITEM.get(ResourceLocation.parse(itemId))MCItemStackCompoundTagCUSTOM_DATACUSTOM_NAMELORENpcAPI.Instance().getIItemStack(mcStack)Use explicit keys in CUSTOM_DATA, for example:
item_typeowner_uuidmain_uuidsession_idrolestateconfig_idcoord_idUse string values by default unless there is a strong reason to use another primitive.
Separate durable item metadata from live workflow logic.
Use:
CUSTOM_DATA
tempdata
Do not try to stuff all runtime orchestration into item metadata if the state only needs to live during the current NPC/player session.
Lore should describe:
Examples:
Use this to bind Configurator and Coordinator.Return this item to Main to finalize linking.Whitelist only.Do not use lore as the primary storage for technical data.
Do not rely on:
setNbt(...) style assumptionsgetItemName() as a stable item idFor durable identity and workflow logic, always read CUSTOM_DATA.
var NpcAPI = Java.type("noppes.npcs.api.NpcAPI");
var BuiltInRegistries = Java.type("net.minecraft.core.registries.BuiltInRegistries");
var ResourceLocation = Java.type("net.minecraft.resources.ResourceLocation");
var DataComponents = Java.type("net.minecraft.core.component.DataComponents");
var CustomData = Java.type("net.minecraft.world.item.component.CustomData");
var ItemLore = Java.type("net.minecraft.world.item.component.ItemLore");
var Component = Java.type("net.minecraft.network.chat.Component");
var MCItemStack = Java.type("net.minecraft.world.item.ItemStack");
var CompoundTag = Java.type("net.minecraft.nbt.CompoundTag");
function createCustomItem() {
var itemType = BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:paper"));
if (itemType == null) return null;
var mcStack = new MCItemStack(itemType);
var tag = new CompoundTag();
tag.putString("item_type", "example_item");
tag.putString("state", "new");
mcStack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
mcStack.set(DataComponents.CUSTOM_NAME, Component.literal("Example Item"));
mcStack.set(DataComponents.LORE, new ItemLore(buildLore([
"Example description.",
"Technical metadata is hidden in custom data."
])));
var item = NpcAPI.Instance().getIItemStack(mcStack);
if (item != null && !item.isEmpty()) item.setStackSize(1);
return item;
}
When reading technical data back:
item.getMCItemStack()DataComponents.CUSTOM_DATAcopyTag()Do not infer workflow state from lore or display name if CUSTOM_DATA already exists.
When items belong to linked NPC workflows, prefer:
main
tempdatasub
tempdataThis keeps ownership clear and avoids ad hoc item mutations.
When a custom item fails, check:
BuiltInRegistries.ITEM?CUSTOM_DATA actually written?NpcAPI.Instance().getIItemStack(mcStack)?CUSTOM_DATA, not lore or display name?If the user asks for implementation, prefer a full ready-to-paste item factory or full script, not partial fragments.