Custom items and the resource pack

The 122 registered custom items, the surfaces they are locked out of by default, and the resource pack that gives them models.

What the registry holds

Every item on the server that is not plain vanilla is a row in one registry. It holds 122 entries. Of those, 58 are buildable: the plugin can construct a fresh copy on demand from a factory, which is what the admin shop catalogue, the starter kits and the food line all rely on. The remaining 64 are metadata-only. They have no factory, and asking the registry to build one returns nothing.

That split is a design decision, not an omission. A forged longsword is a per-instance object: it carries its own stats, its own sockets, its own runes and its own unique id, so there is no single canonical copy to hand out. Gems, rune stones, expedition badges, money notes and bullion bars are metadata-only for a sharper reason: a factory would let the canonical name-and-lore refresh overwrite a real note's printed worth with a sample value. The note would still redeem correctly, but it would lie about itself in your inventory. Registering the family metadata-only removes the whole class of problem, and closes a free-item route at the same time.

Identity

An item's identity is a persistent data tag, custom_item_id, written into the stack. The older custom model data string is kept as a permanent read-only fallback, and items convert to the tag lazily as they pass through a read site. Two things are never rewritten:

  • Anything that already carries the tag. A forged item's id is unique per instance, something like bl_longsword_emma_a1b2c3d4, and the rune engraver, the socket handler and the enchant station all read it directly. Overwriting it with a type id would de-identify the item for every one of those systems.
  • A registry id that differs from the declared tag value. The rift ore fragment carries bl_rift_ore_fragment but registers under blacklight:bl_rift_ore_fragment; writing the id would replace a correct tag with a different string.

Recipes that take a custom item as an ingredient declare both forms, the legacy sample and the tagged one, so nothing stops matching partway through the conversion. The cost of doing it lazily is stated plainly: items sitting in unloaded chunks, offline inventories, ender chests, shulker boxes, item frames, graves, market and shop listings, company shelves and NPC carry are converted only if and when they are touched. The legacy read path is therefore permanent. It is not scheduled for removal.

Archetype is declared, never guessed

Every custom item is built on a vanilla base material, because that is what makes it render. The base material is a rendering decision. What the item is for is a separate, required, human-written declaration called its archetype, and it is never inferred from the base.

The reason is a defect class that shipped three times. Cured Cannabis was edible because DRIED_KELP is edible. The Forge Hammer smelted down into nuggets because IRON_AXE does. The Hemp Leaf cooked into Dried Kelp because KELP does. In every case the item inherited behaviour nobody asked for, from a material chosen for its texture.

All 122 registrations now declare one of eighteen archetypes.

ArchetypeWhat it isConsuming surface it opens
FOODReal food: nutrition, saturation, eat timeEAT
DRINKDrunk rather than eaten; same event, different animationEAT
TOOLMines. Tool component, mining rules, durabilityBLOCK_TRANSFORM
WEAPONHits. Attack damage and attack speednone
ARMORWorn. Equippable slot, armour and toughnessnone (opens the ARMOR surface)
PLANTPlanted onto valid ground and registered as a cropnone
SEEDThe plantable half of the agriculture pairnone
BLOCKPlaces exactly one defined blockPLACE_BLOCK
MATERIALA crafting input and nothing elsenone
CURRENCYMoney. Carries a worth value and a redeem pathnone
UTILITYRight-click fires one programmed actionnone
KEYSpent at exactly one station or systemnone
BOOKReadable: a Java book or a Bedrock formnone
SCHEMATICCarries build data for a builder NPCnone
RELICValue and lore only; a turn-in targetnone
CONTAINEROpens its own inventorynone
FUELBurns, on purposeFUEL
MISCAn object that existsnone

The archetype is a separate axis from the catalogue category. The category has eleven values (forge, rift, ruin, grove, expedition, food, utility, tool, gem, rune, shop) and answers "which subsystem does this come from"; it drives the shop catalogue and the pack generator. The archetype answers "what does this do". A Rift Shard is category rift and archetype material; a Ruin Key is category ruin and archetype key. Deriving either from the other would silently re-file items in both, so they stay independent and a boot check asserts the pairing is not contradictory.

The 21 surfaces an item can be spent on

There are 21 places in Minecraft where an item is consumed, converted or handed away. All 21 are denied by default for a custom item. An archetype has to opt in, one surface at a time. The reasoning is asymmetric and that asymmetry is the whole argument: refusing a surface costs you nothing, while allowing one you did not mean to allow either destroys the item or launders it into something the economy did not price.

SurfaceWhat allowing it would mean
EATEaten or drunk
PLACE_BLOCKPlaced as a block
PLACE_ENTITYPlaced as an entity
COOKSmelted, smoked or campfire-cooked
FUELBurned as fuel
CRAFTUsed as a recipe ingredient
SMITHUsed at a smithing table
ANVILUsed on an anvil
GRINDSTONEGround down
STONECUTCut on a stonecutter
LOOMUsed on a loom
CARTOGRAPHYUsed on a cartography table
BREWBrewed
ENCHANTEnchanted at a table
TRADETraded to a villager
BEACON_PAYPaid into a beacon
COMPOSTComposted
DISPENSEDispensed
BONE_MEALUsed as bone meal
BUCKETUsed as a bucket
FRAMEPut in an item frame

A twenty-second deny-by-default surface was added later: BLOCK_TRANSFORM, the block-changing right-click a base material defines. Till, path, extinguish, strip, scrape, de-wax, shear and ignite are eight different actions and six of them fire no event of their own, so they share one hook and are named once rather than eight times. Only the tool archetype seeds it, which is why a Cultivator Hoe can till while a bank note built on a gold block cannot.

The six surfaces that work the other way round

Six surfaces are seeded permissively and have to be opted out of: interacting with a block, interacting with a creature, combat, projectiles, taking durability damage, and being worn as armour. Denying these does not protect the item, it breaks it. A blanket block-interaction deny would mean you cannot open a chest while holding a Home Scroll. A blanket combat deny would mean you cannot punch a zombie while carrying Hemp Fiber. The exception that earns the distinction is armour: only the armour archetype seeds it, which turns "a bank note is not a helmet" into something the server enforces instead of something a comment claims.

Combat is deliberately not denied for currency, and the reasoning is written down rather than assumed: a gold-block note swung at a zombie deals a bare-hand hit and launders nothing, while denying combat would stop you defending yourself for the crime of carrying money.

The crafting rule

Two conditions must both hold before a custom item can go into a recipe: the item declares the crafting surface, and the recipe names that specific id. The allow-map is derived from the live recipe at match time, never hand-maintained. The second condition is what stops Hemp Cloth quietly satisfying a recipe that asked for Hemp Rope, since the two share a paper base. Six ids currently declare crafting: the spawner fragment, the rift ore fragment, glow essence, hemp fiber, hemp cloth and cured cannabis. Two ancient templates declare smithing, and the two efficiency books declare anvil use.

What a refusal looks like in play

A denied surface does not fail silently. It teaches. The line names what the item is, what you tried to do with it, and what it is actually for:

That's a currency, it can't be composted. It's money. Right-click to redeem it, or spend it.

Every archetype has its own closing sentence. A key gets "it's spent at the one station or system it belongs to". A relic gets "its value is in owning it, or turning it in". A schematic gets "give it to a builder NPC". The refusal ends in guidance rather than in a wall.

The throttle, and why it is keyed the way it is

The hint is limited to one line per player, per surface, per 10 seconds. The per-surface part matters. An earlier version shared one window across all surfaces, so a player who bounced off the enchanting table and walked to a grindstone was refused there in total silence, which is exactly the moment someone is working out what an item does. Keying it per surface keeps the anti-spam property that mattered anyway: a hopper feeding a furnace still produces at most one line every ten seconds, because it is refusing the same surface every time.

Refusals nobody is holding

A hopper-fed furnace, composter or brewing stand has no player who did it. The guard looks for whoever has that block's inventory open, falls back to any player within 6 blocks, and stays silent if nobody is watching. A refusal shouted into an empty room is noise.

Guards run late in the event chain and skip anything another system has already handled. That is why the money-note redeem click, the hemp leash and the vote case open are not refused by their own guards: those subsystems cancel the event first, so the guard never sees it. It is what lets the rules be blanket rules instead of a growing list of special cases.

The checks that run at boot

A rule that is only written down drifts. Six self-checks run every time the server starts, walking all 122 registrations and reporting what they find. They log; they never abort the boot, because killing an otherwise healthy server over a diagnostic is the worse failure.

#What it asserts
1Every entry declares an archetype. No defaulting, no inference.
2Every entry resolves to a real capability map.
3Archetype and catalogue category do not contradict each other.
4Every surface has at least one registered guard. An unguarded surface is a permission that cannot be enforced.
5Crafting declarations match the live recipe set in both directions.
6Every edible archetype sits on a base material vanilla will actually fire a consume event for.

Check 5 is the one worth explaining, because "in both directions" is doing real work. It reads the live recipe list and compares it against the declarations twice. An item that declares crafting but is named by no registered recipe is reported: the declaration is stale, or its recipe failed to register. An item that is named by a registered recipe but does not declare crafting is also reported, and that is the dangerous direction: the guard would refuse a recipe that works today, and the first symptom would be a player's recipe quietly returning nothing.

Three more arms have been added since. Check 7 hunts the opposite fault to all the others: it looks for over-blocking, reporting any item whose base material performs a vanilla block transform but which does not permit it, because that is a tool that has silently lost the thing its base is for. Check 8 exists because checks 7 and 7f read the declared base while the runtime reads the re-tiered one, and a check that is honest about the table it reads and silent about the table that decides is how releases get lost here. Check 9 asserts every forged armour piece is trimmable at every tier, because the trim permit becomes unreachable if a piece is ever built on a material vanilla will not trim, with no error and no other symptom.

The resource pack

The models live in a separate pack called blacklight_items. It declares pack format 101, minimum and maximum, which is the Minecraft 26.1 item model format. It is hosted rather than bundled into the plugin, and the server pushes it to your client on join.

Part of the packCountWhat it is
Vanilla base overrides51One file per base item, routing custom ids to custom models
Custom item definitions93The definition a stamped item model resolves to
Custom models115The model files those definitions point at

Selection is driven by a string property. Each overridden base item carries a minecraft:select model keyed on minecraft:custom_model_data, with named cases mapping each custom id to a Blacklight model, and vanilla's own model preserved as the fallback. If a stack carries no custom id, or carries one this base does not list, it renders exactly as vanilla.

Base itemCasesWhat routes through it
arrow9The eight forged ammunition types plus the bolt
amethyst_shard8The gems and rune stones that sit on this base
iron_sword7Dagger, shortsword, longsword, saber, rapier, chain blade, generic weapon
iron_axe5Forge Hammer, halberd, warhammer, battleaxe, lumber axe
iron_pickaxe5Excavation, precision and tunneling picks, heavy hammer, generic tool
bread5The sandwich line, raw and cooked
nether_star3Forged relic, Dawn Sunder, ancient relic
paper3Home Scroll and the two ancient templates
gold_block, gold_ingot, gold_nugget, iron_nugget1 to 2 eachThe four money notes and the relic coin
structure_void1Spawner fragment
spyglass1Chunk reveal token

The pack is generated from the registry, not maintained by hand. The plugin exports the live item set to a manifest and the generator reads it, so the pack cannot drift into listing items the server does not have or missing items it does.

Five bases deliberately left vanilla

Five vanilla item files are never overridden: bow, crossbow, trident, shield and fishing_rod. Their vanilla definitions are not flat models, they are predicate trees. The bow's file branches on pulling and pull progress; the crossbow's on charged and firework; the trident's on the throwing pose; the shield's on blocking; the rod's on cast state. Writing a flat override on top of one of those replaces the whole tree, for every holder of that base item on the server, custom or not.

This is not a hypothetical. A shipped trident.json had already cost every vanilla trident on the server its throwing pose, and its single custom case could never have matched anyway, because the forged spear is built on a spear material and never on a trident. A shipped shield.json had cost every shield its blocking pose. Both files were deleted rather than left in place, because a refusal that leaves the bad file on disk changes nothing.

The trade-off is stated rather than buried. Custom items sitting on those five bases render as ordinary vanilla items: the five expedition badges are on the shield base, so they look like shields. Their identity is their name and lore, which is what a Bedrock player has always seen. Weighed against that, breaking the draw animation for every archer on the server is the worse outcome.

The generator does not decide this from a hard-coded name list. Where a copy of vanilla's own file is available it reads that file's shape: a flat model is safe to wrap flat, anything else is a tree and gets the entire vanilla model preserved as the fallback with the custom cases alongside. Mojang can add a predicate to any base in any version, and the file is the only thing that knows. The name list survives only as the fallback for when no vanilla file is on hand.

A model only ships behind real art

There are two ways to point an item at a custom model. The older one is the selection case above, which depends on the base item. The newer one stamps an item model component directly onto the stack, which replaces the whole model definition and is therefore independent of the base: a netherite forged pick can render its forged model rather than a vanilla netherite pick. Both are used, deliberately, so a client that ignores the component still has the selection path to fall back on.

The stamp is gated on one question: does this id have real artwork behind it? The reason is a defect that looked green from every angle. Every link in the chain was checked and every link resolved: the component pointed at a definition, the definition pointed at a model, the model pointed at a texture, and the texture existed. Nobody asked what was in the texture. Every image in the pack was a 1 by 1 magenta placeholder of 69 bytes, and magenta is exactly the colour a client paints when a texture is missing. A structurally perfect chain ending in a magenta pixel is indistinguishable from the broken cube the chain check existed to prevent.

The rule that came out of it is short: a stamp with no artwork behind it renders worse than no stamp at all. Unstamped, a forged sword falls back to a readable iron sword carrying its own name, lore, rarity, stats, sockets and runes. Stamped with nothing, it is a cube.

So the generator now walks a fourth link. It checks that the texture file is larger than 200 bytes, which a placeholder never is and real art always is, and writes the answer into a manifest the plugin reads. The plugin has no copy of the pack, so the manifest is the only thing it can ask. A missing or unreadable manifest is treated as an empty set, never as an error, because the failure mode of "we could not read it" has to be a readable vanilla item rather than a refused one.

Where this stands today. The manifest lists 93 ids, and none of them has final artwork yet. Every texture in the pack is still a placeholder. That means nothing is currently stamped, and custom items render as their vanilla base with their own name, lore and stats. That is the correct outcome for the current asset state, and it is printed at boot in as many words. The moment real art lands and the pack is regenerated, the stamp returns on its own, with no code change.

Without the pack, and on Bedrock

Nothing mechanical depends on the resource pack. If you decline it, if it fails to download, or if you are on Bedrock, every custom item behaves exactly the same. Its name, its lore, its rarity colour, its stats, its sockets and its runes are all still there, because all of that is server-side data written into the item and not into the pack.

Bedrock is the honest test of this, and it has always been the design constraint rather than an afterthought. Custom model data strings are a Java client mechanic; a Bedrock client sees the vanilla model whatever it accepts. So the rule the whole item system is written against is that an item's identity has to survive in its name and its lore alone. A Sigil Fragment's lore names where the next one is. Ore identity lives in the name. Nothing important is ever encoded only in a texture.

Custom names are rendered through one builder, which is why they look consistent. Names are white and non-italic unless they ask for another colour, lore lines are grey and non-italic, and rarity is set explicitly. That last one is not cosmetic bookkeeping: an item built on an enchanted book or a nether star inherits vanilla's rarity colouring and its name comes out yellow or aqua whatever colour it asked for, so rarity is stated rather than left to the base material. Italics are stripped recursively, because turning italics off on a line only affects that node and a lore line built by joining pieces goes italic from the join onward.

Much of the item guard system and the whole artwork gate are marked code-written and not server-verified in the plugin's own documentation. The numbers, names and limits on this page are traceable to that documentation and to the pack on disk. Treat behaviour you have not seen yourself as documented rather than as observed.