Custom items and the resource pack
The 209 registered custom items, the surfaces they are locked out of by default, the custom blocks the Garden added, and the resource pack that gives all of it models.
What the registry holds
Every item on the server that is not plain vanilla is a row in one registry. It holds 209 entries. Of those, 129 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 80 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, 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 209 registrations 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 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, and it keeps both now that it is retired. 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.
Two of those eleven are leftovers worth naming, because they are the reason the count is still eleven. The category that once labelled the dungeon line survived the removal of that line, since four items filed under it belong to the star navigation and cartography systems instead: the star map, the astrolabe, the cartographer's quill and the waypoint marker. Deleting the constant would have silently re-filed four live items in both the shop catalogue and the pack. Counting the registrations that survive, and keeping the constant if any does, is the rule that decided it, and the ruin category was decided the same way: update 2.0.1 removed the Ancient Ruins, six registrations filed under that category outlived them, so the constant stayed.
The ruin registrations, and what each one is now
The subsystem those items came from is gone. The registrations are not, and that half is deliberate: an id the registry has forgotten turns a held item into an anonymous nugget wearing somebody else's name. If one of these is sitting in your chest, this is what it is today.
| Item | What it is now |
|---|---|
| Relic Coin | A live currency, and the only one of this group that is. What changed is where it comes from, not whether it exists: the grove pays one coin per fifty glow flowers, and a cleared rift pays per tier. It is spent at the Grove Exchange, which burns it, and a sneak right-click still sells it at its price. |
| Ruin Key | Retired, not deleted. The registration is kept so a key in a chest still knows what it is, but it opens nothing and it can no longer be crafted, because a key that opens nothing should not have a recipe. A sneak right-click sells it back at its own recipe's material price, four gold ingots and four amethyst shards, priced by the economy's material oracle. Keys in your inventory, your ender chest and one level of shulker boxes are swept on join as well. No vote case can hand out another one. |
| Ruin Map | Registration kept, metadata-only. It sits on a filled map and carries a name, lore and a model, but no map view was ever bound to it, so it points at nothing and never did. |
| Sigil Fragment | Registration kept, metadata-only. The per-site machinery that used to write it went with the subsystem; the id it declared is unchanged, and the id is the identity. |
| Ancient Relic | Registration kept, metadata-only. It keeps its name, its lore and its nether_star model. |
| Lore Tablet | Registration kept, metadata-only. Its name and its lore are preserved exactly as they were. |
The last four are keepsakes and are not bought back, because none of them was ever bought and there is nothing to return. A sneak right-click on air says so, and says that the Key is the one that sells.
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 209 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 all-buildable check, and where it runs
One more check sits apart from the nine, because it does something the others do not: it actually builds things. The all-buildable self-check asks every registration that declares a factory to produce a real stack, and the current boot line reads 129 of 129 OK. It was 71 of 71 a release family ago, which is the plainest single number for how much the registry has grown.
Where it runs is the interesting part. It used to sit inside the enable pass, and one boot log showed the same factory failing at one point and succeeding seconds later in the same start, because the subsystem it reached for had not been wired yet. Anything that produces an object now runs at the post-enable tail instead. The same log turned up a worse case in passing: one tier-5 forge component had been registered metadata-only under a comment that had the rule exactly backwards, so the item could never be constructed and its recipe was refused every single restart. The boot line had been saying so all along.
The resource pack
The models live in a separate pack called blacklight_items. It is hosted rather than bundled into the plugin, and the server pushes it to your client on join. The published pack is 797 files and 280 textures, built from a sorted file list with a fixed timestamp, so two releases of identical content produce byte-identical zips and the same checksum.
Two registers, and a bare integer that says which of neither
The pack declares the RESOURCE format 88 for Minecraft 26.2, as a range rather than a point: min_format 84 to max_format 88. A datapack at the same version declares 107.1. Those are two completely different registers, and this is the part worth reading twice: both are bare integers in JSON, and nothing in the file says which of the two questions it is answering.
That cost a release. The pack had been declaring 101, minimum and maximum, and 101 is the data major for 26.1, not the resource one. The resource number for that version was 84. Wrong axis entirely, and anybody who checked 101 against a changelog would have found it and confirmed the bug, because 101 is a real number that means a real thing in the other register. The fix was not to hand-edit 101 into 84, since a hand-edited 84 is the same artefact one digit luckier. The file is now written by the pack generator from a single named constant that carries its own register, and a check asserts that the two registers' majors never overlap: the moment one number could be both, the question "is this a resource number?" stops being answerable from the number at all.
The same subtlety bites in the other direction. A bare integer maximum is not a point value, which is why a pack declaring max_format 107 still loads at 107.1. Had it meant 107.0, one of the datapacks below would refuse to load on the very version it is named for.
The four datapacks
Four datapacks ship with the server and are pinned to exact versions. All four load at 26.2's data format 107.1.
| Datapack | Declared data range | What it brings |
|---|---|---|
| Terralith 26.2 v2.6.4 | 107 to 107, so 107 and up within that major | The densest overworld structure placer of the four, and the terrain generation the overworld is built on |
| Dungeons and Taverns v5.3.2 | pinned to exactly 107.1 | The largest structure set, 32 of them, 26 placed in the overworld |
| Epic Dungeons 1.2.5 | 107 to 107 | Four structure sets. It shipped once declaring only the legacy format 15 and would not load on anything; the repacked zip loads |
| Endercon 3.2 | 41 to 200.10 | End content. It contributes nothing to the overworld |
Two things about that set are worth knowing rather than discovering. Endercon deletes vanilla's End City placement and re-adds its own about three times denser on the same salt, which is a live balance change arriving inside a datapack rather than behind a setting. Parts of it are also built for a modded loader that a Paper server has no reader for, so those files are ignored without any error, and its End content should not be assumed to work. Epic Dungeons ships its whole structure set twice, once under the old directory names and once under the current ones, which roughly doubles its size, and one room exists only under the dead spelling.
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 | What routes through it |
|---|---|
arrow | The forged ammunition types and the bolt |
amethyst_shard | The gems and rune stones that sit on this base |
iron_sword | Dagger, shortsword, longsword, saber, rapier, chain blade, generic weapon |
iron_axe | Forge Hammer, halberd, warhammer, battleaxe, lumber axe |
iron_pickaxe | Excavation, precision and tunneling picks, heavy hammer, generic tool |
iron_hoe, iron_shovel | The forged hoes and shovels, the Cultivator Hoe among them |
bread | The sandwich line, raw and cooked |
nether_star | Forged relic, Dawn Sunder, ancient relic |
paper | Home Scroll and the two ancient templates |
clock | The Astrolabe, which is clock-based and not spyglass-based |
spyglass | Chunk reveal token |
gold_block, gold_ingot, gold_nugget, iron_nugget | The four money notes and the relic coin |
structure_void | Spawner fragment |
One base lost its entry entirely at the last regeneration. compass.json had carried exactly one case, for an item that is no longer registered anywhere, and the generator dropped the stale route on its own. That is worth distinguishing from the refusals further down: here a removal was correctly a removal.
The pack is generated from the registry, and the roster it read was two thirds short
The pack is generated, not maintained by hand. For a long time that was less true than it sounded. The manifest of live items had exactly one producer, a command that needs a running server, and without one the generator fell back to a built-in list its own source called stale. Nobody had measured how stale. Measured: the registry holds 209 items and the fallback listed 75. About two thirds of the roster was invisible to every offline pack run, and two entries in the fallback named items that are no longer registered at all.
The fix is possible because registration declares rather than produces: the registry can be bootstrapped with no server at all, and an export tool writes a real manifest in about a second. It refuses rather than writing a plausible file, since a failing self-check, an unbootstrappable registry or a roster of zero would each produce a pack with no custom item in it and a successful exit code. The first real manifest immediately found six bullion items naming textures that do not exist, which renders as the purple-black missing-texture cube on both platforms, and a fallback shape that six in ten items were being drawn with because no rule matched them.
The block half, and the Garden
The registry used to hold only items. The Garden added blocks, and they are declared under the same rule: a Blacklight block is a position with a texture and a name, and it has exactly the behaviours it declares. Block archetypes are PLANTER, STATION, DECOR, TRELLIS, HIVE, SOIL and SERVERY, declared and never inferred from whatever the block is hosted on, for the same reason an item's archetype is.
Two custom blocks are registered today: the Planter Box and the Garden Trellis. Both are hosted on a note block, each claiming one specific note block state.
Identity is positional, and the ledger is append-only
A note block cannot carry item data, so there is nowhere on the block to write "this is a Planter Box". The record lives in the chunk instead. It travels with the chunk, survives restarts with no write of its own, and adds no new save path, which is the property that won the argument. Asking "is this one of ours?" on a block break is an in-memory lookup first, and the chunk record is only read on a hit.
The map from custom block id to host state is append-only and never reordered, and this is the invariant the whole design exists to protect. Reordering it does not break a build and logs nothing at all. It silently re-skins every one of those blocks already standing in the world: your planter becomes something else on the next restart. A committed ledger file and a build guard assert that no existing entry ever changes.
What a Bedrock player sees is declared, not derived. Custom block models are Java-only, so somebody has to decide which vanilla block stands in, and a default would be a guess about the one client a resource pack cannot correct. The Planter Box declares a composter and the Garden Trellis an oak fence. Every registration call takes that fallback, so forgetting it is a compile error rather than a boot report.
The generator's block half
The generator writes blockstates/note_block.json, and the shape of that file is the argument. A note block has 1,350 states. Two of them are claimed. Writing only those two would leave the other 1,348 invisible for every player on the server, including everyone who owns no custom block at all, so the file is written in full: two claimed, 1,348 preserved pointing at vanilla's own model. For one release it was refused outright by default, because no copy of vanilla's own file could be obtained on the build machine and a guess is not a copy. The assumption is now checkable rather than silent: a real vanilla file wins where one is supplied, and the run prints both the assumed value and the real one so a disagreement is visible.
For a long while the block half wrote nothing at all, and the reason was not that it was unbuilt. Adding a block takes three edits in one commit: the table in code, its ledger file, and the registration. Only two were ever made. Without the ledger file the generator's block half stopped at "ledger absent" and emitted no note block file, and the guard that checks the ledger failed on its own missing input. The block half was refusing because it had nothing to read, which is a different thing from a feature that does not exist. Creating that one file turned it on.
Plant looks
Plants get a half of their own. The plant ledger holds 64 looks over 19 species: 60 crop looks, plus the four grove maturity markers allocated last into the states left over. The generator draws a model and a texture for every one of them and then re-skins the host, writing blockstates/tripwire.json with 128 variants. A growing plant is a tripwire carrying a reserved state that the pack paints as the plant, and the reserved states deliberately leave the powered flag and the four direction flags free.
Two looks sharing one texture would be two things a player cannot tell apart, and a hash into buckets does not guarantee they differ, so each look's unique ledger position is stamped into four corner pixels. Any two looks then differ in at least one pixel by construction rather than by luck.
A Java client holding the pack sees the plant. A Java client without it, and every Bedrock client, is sent the vanilla crop instead, per viewer, which is an illusion that changes no world state at all. What the plant is worth, how fast it grows, what it yields and what its genetics are come from the plot record and are identical either way. Only the picture differs. The growing side is the garden plants article.
Placeholders, not art
The generator no longer draws stand-in pictures. Where a texture has not been authored it writes a 1 by 1 fully transparent placeholder and leaves the drawing to a person. It used to write a 1 by 1 magenta square, which is exactly the colour a client paints when a texture is missing, so a structurally perfect chain ending in one was indistinguishable from a broken cube. Any 1 by 1 image counts as a placeholder in any encoding, which is how the older magenta ones are recognised and replaced rather than preserved as art.
The pack keeps every selection case regardless of what is drawn. An earlier attempt dropped the case for an item with no art so it would fall back to its vanilla base, and that gate read the texture on whichever machine ran the generator. The generator runs in a sandbox without the textures, so it dropped every case and every custom item lost its art in one run. Whether an item renders its own model or its vanilla base is a decision the server makes at stamp time, not one the pack makes based on which files happened to be on disk.
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, and it is the defect that keeps coming back. A shipped shield.json had cost every shield on the server its blocking pose. A shipped trident.json had cost every vanilla trident its throwing pose, and its single custom case could never have matched anyway, because the forged spear had long since moved onto a spear material.
The trident file is the one worth dwelling on, because it survived the release that found it and broke every trident on the server for five more. The code that deletes a refused base's stale file lived inside the loop over base materials, and that loop only visits a base the roster still has items on. The moment the forged spear moved off the trident base, the deletion became unreachable and the bad file became permanent. A refusal that only works while the base is occupied stops working exactly when the stale file matters most. The generator's own self-check had been reporting the file correctly and unheard the whole time. Both files are now gone, and the sweep walks all five bases on every run, occupied or not.
The trade-off is stated rather than buried. Custom items sitting on those five bases render as ordinary vanilla items: the forged bows sit on the bow base, so they look like bows. 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.
The size test was wrong, and the fix is an identity test
The first version of the gate asked whether the texture file was larger than 200 bytes, on the reasoning that a placeholder never is and real art always is. Measured against the actual pack, that is false in both directions. Two genuine 16 by 16 textures came in at 185 and 194 bytes and were being excluded for being fifteen bytes too small, so the plugin had been refusing to stamp artwork that exists. A deliberate 8 by 8 transparent spacer glyph of 73 bytes would have been deleted outright by any rule that adopted small files as placeholders, which is the destroy-the-artwork disaster rebuilt out of a heuristic inside the very code written to prevent it.
The question is now asked of identity rather than size. The placeholder is deterministic, so the writer compares the bytes on disk against it: nothing there means write, our placeholder exactly means overwrite, and anything else, however small, means preserve. The 200-byte figure survives only as a printed signal, because "this texture is suspiciously small" is still worth an operator's attention, and it is no longer the gate.
Where this stands today. The gate answers in three states, not two. Ours is a texture this run wrote, adopted or confirmed, and it is never stamped. Authored is a texture with a recorded receipt whose bytes on disk now differ from it, and that is stamped. Unknown is everything else, and it is not stamped. The asymmetry decides it: an unknown that is really artwork costs one texture its stamp, while an unknown that is really the generator's own drawing and gets stamped is the exact defect the rule exists for. No id currently has confirmed authored art, so nothing is 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. Draw over a texture that has a receipt, regenerate, and 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. The same holds for the blocks and the plants: what a plot yields and what a planter does are the record's, and only the picture is the pack's.
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 Ruin Map's lore differs from copy to copy, which is exactly why it is registered metadata-only: the lore is the item. 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, the whole artwork gate and the block and plant halves of the pack 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.