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_fragmentbut registers underblacklight: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.
| Archetype | What it is | Consuming surface it opens |
|---|---|---|
FOOD | Real food: nutrition, saturation, eat time | EAT |
DRINK | Drunk rather than eaten; same event, different animation | EAT |
TOOL | Mines. Tool component, mining rules, durability | BLOCK_TRANSFORM |
WEAPON | Hits. Attack damage and attack speed | none |
ARMOR | Worn. Equippable slot, armour and toughness | none (opens the ARMOR surface) |
PLANT | Planted onto valid ground and registered as a crop | none |
SEED | The plantable half of the agriculture pair | none |
BLOCK | Places exactly one defined block | PLACE_BLOCK |
MATERIAL | A crafting input and nothing else | none |
CURRENCY | Money. Carries a worth value and a redeem path | none |
UTILITY | Right-click fires one programmed action | none |
KEY | Spent at exactly one station or system | none |
BOOK | Readable: a Java book or a Bedrock form | none |
SCHEMATIC | Carries build data for a builder NPC | none |
RELIC | Value and lore only; a turn-in target | none |
CONTAINER | Opens its own inventory | none |
FUEL | Burns, on purpose | FUEL |
MISC | An object that exists | none |
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.
| Surface | What allowing it would mean |
|---|---|
EAT | Eaten or drunk |
PLACE_BLOCK | Placed as a block |
PLACE_ENTITY | Placed as an entity |
COOK | Smelted, smoked or campfire-cooked |
FUEL | Burned as fuel |
CRAFT | Used as a recipe ingredient |
SMITH | Used at a smithing table |
ANVIL | Used on an anvil |
GRINDSTONE | Ground down |
STONECUT | Cut on a stonecutter |
LOOM | Used on a loom |
CARTOGRAPHY | Used on a cartography table |
BREW | Brewed |
ENCHANT | Enchanted at a table |
TRADE | Traded to a villager |
BEACON_PAY | Paid into a beacon |
COMPOST | Composted |
DISPENSE | Dispensed |
BONE_MEAL | Used as bone meal |
BUCKET | Used as a bucket |
FRAME | Put 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 |
|---|---|
| 1 | Every entry declares an archetype. No defaulting, no inference. |
| 2 | Every entry resolves to a real capability map. |
| 3 | Archetype and catalogue category do not contradict each other. |
| 4 | Every surface has at least one registered guard. An unguarded surface is a permission that cannot be enforced. |
| 5 | Crafting declarations match the live recipe set in both directions. |
| 6 | Every 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 pack | Count | What it is |
|---|---|---|
| Vanilla base overrides | 51 | One file per base item, routing custom ids to custom models |
| Custom item definitions | 93 | The definition a stamped item model resolves to |
| Custom models | 115 | The 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 item | Cases | What routes through it |
|---|---|---|
arrow | 9 | The eight forged ammunition types plus the bolt |
amethyst_shard | 8 | The gems and rune stones that sit on this base |
iron_sword | 7 | Dagger, shortsword, longsword, saber, rapier, chain blade, generic weapon |
iron_axe | 5 | Forge Hammer, halberd, warhammer, battleaxe, lumber axe |
iron_pickaxe | 5 | Excavation, precision and tunneling picks, heavy hammer, generic tool |
bread | 5 | The sandwich line, raw and cooked |
nether_star | 3 | Forged relic, Dawn Sunder, ancient relic |
paper | 3 | Home Scroll and the two ancient templates |
gold_block, gold_ingot, gold_nugget, iron_nugget | 1 to 2 each | The four money notes and the relic coin |
structure_void | 1 | Spawner fragment |
spyglass | 1 | Chunk 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.