Plants as Real Blocks

Every planted species now has its own block state, its own picture and its own record. Here is what that means when you plant, look at, hit or step on one.

A block that is a position, not a thing

Adding an item to Minecraft is straightforward. Adding a block is not. There is no place on an ordinary block where a plugin can write anything down: a note block cannot carry hidden data the way a chest can, and the code that tries to give it some compiles perfectly and then quietly does nothing.

So a Blacklight block is a position that carries a texture and a name, and it has exactly the behaviours it declares. Nothing is inferred from the material. The record lives in the chunk's own storage, so it travels with the chunk, survives a restart without a separate save file, and is only read when something actually happens at a tracked position.

Each block declares a behaviour class. Every class already covers breaking, being interacted with, being blown up and supporting what sits on top; the class decides what is added on top of that.

ClassWhat it adds
PLANTERGrowth, bone meal
STATIONOpening a container
DECORNothing beyond the four basics
TRELLISGrowth
HIVEHopper input and output, opening a container
SOILGrowth, bone meal, entities changing the block
SERVERYHopper input and output, opening a container

Two blocks use this system today, both hosted on a note block, both craftable from plain vanilla ingredients. They are garden furniture rather than a reward, and they are priced that way.

BlockRecipeWhat a Bedrock player sees
Planter BoxFour planks around any dirt family blockA composter
Garden TrellisA lattice of sticks and string, or of bambooAn oak fence

Their descriptions say what the block is, not what it does. Vanilla will not hold a plant on a note block, will not till it and will not hydrate it, so plant in it, bone-meal it, harvest from it was a promise the block could not keep. The text was corrected rather than the promise faked.

Three things that are true and are not hidden

  • Break speed comes from your client. It is calculated from the hardness of the host block, and the server cannot cleanly override it. A custom block breaks at the speed of whatever is underneath the texture.
  • Bulk world editing bypasses the normal block events. A mass paste or fill neither creates nor clears records. Staff can run a rescan over a radius to reconcile a region, and that is written down rather than presented as solved.
  • Redstone repowering the host would change its state. The server needs one Paper setting for that, and the boot log checks that it is on.

Drawing a custom block, and the three refusals

The resource pack is built by a generator rather than assembled by hand. For each block it writes a block model, a block texture, an item definition, an item model and an entry in an artwork manifest, and it sweeps out art belonging to blocks that no longer exist.

It also refuses to write three files when it cannot write them correctly, prints the reason, and deletes any stale output it finds. The refusals are the interesting part, because each one is a way this pack could quietly break blocks that have nothing to do with the garden.

The atlas

An atlas file tells the client which textures to stitch together. Writing one under the plugin's own namespace defines an atlas that nothing loads: a green-looking file that changes nothing. Writing one under minecraft replaces vanilla's, whose list covers block and item textures, paintings, particles, chests, beds, banner patterns, shulker boxes, decorated pots, signs and conduits. Getting that wrong does not lose a garden texture, it loses all of them. The generator refuses, and the supported route is to hand it vanilla's own file to extend.

The note block states

The file that maps note block states to models also replaces vanilla's. The note block has 27 instruments, 25 notes and a powered flag, and the plugin claims exactly two of those states. Writing only the two makes every other one invisible for every player on the server, including players who own no custom block at all. So every key is written out in full, and when vanilla's file is not available to copy from, nothing is written at all.

That drop-in is now in place, so a Planter Box renders as itself on Java once the pack is loaded.

Bedrock. Custom models are a Java feature. A Bedrock viewer is sent a declared vanilla stand-in for each block instead, per viewer, and the name and the description carry the identity. Nothing about the block's behaviour changes with the client you are on.

Every planted thing gets its own block

Before update 2.0.3 a planted species borrowed a vanilla crop block. Rye, barley and frostlace were all wheat, and three different plants stood in a field looking like one. The record knew which was which; the world did not.

Now every look has a block state of its own. The host is minecraft:tripwire. It has seven boolean properties and therefore 128 states, and vanilla only uses the attached flag and the four direction flags to choose a model. The half of the state space where disarmed is true is 64 states that vanilla draws no differently, and those are the ones the plugin reserves.

The blockstates file writes all 128 out explicitly: 64 point at a plant model, and the other 64 point at vanilla's own model for that state, rotation included. A plain resting wire renders exactly as it always did.

The ledger, and why its order is frozen

A ledger maps a look, meaning the combination of species, drawn stage and part, to one reserved state. It is append only. Reordering it would not break a build; it would silently re-skin every plant already standing in the world on the next restart, with nothing logged. A build guard compares the committed ledger against the previous version and fails if an existing entry changed.

Original host blockDrawn looks per speciesSpecies riding it
Torchflower Crop2 stagesemberbloom, goldcrown, hemp
Pitcher Crop3 stages, 2 halves eachnightveil_iris, cannabis
Nether Wart3 stageschili, cinderpetal
Wheat3 stagesfrostlace, rye, barley
Beetroots3 stagessoy, sugar_beet
Sweet Berry Bush3 stagescoffee, tea, saltmarsh_aster
Cocoa2 stages, 4 facings eachvanilla
Cave Vines2 stagesduskbell
Brown Mushroom2 stagescave_mushroom

That comes to 60 crop looks. The four stages of the Grove Seedling marker, Seedling to Sapling to Thicket to Heartwood, were allocated last and fill the remaining four. The ledger is at 64 of 64, with no headroom left, which is why later features reuse an existing look instead of claiming a new one.

Drawn stages are not vanilla ages

Wheat has eight ages and a wheat-hosted plant draws three stages, so the picture is a summary of the record rather than a copy of it. The mapping reserves the last drawn stage for a plant that is actually ripe and spreads the rest below it. An earlier version divided ages evenly and put a wheat-hosted plant on the ripe picture at age 6 of 7, so the plant looked finished while the readout said otherwise. The picture and the readout now come from one rule, and a check walks every species at every age to prove it.

A two-tall species, meaning nightveil_iris and cannabis, occupies two blocks and both of them are recorded. They are written together, carry the same age, and show the same stage. A two-tall plant is two blocks tall at every stage, including the first.

Where ripeness lives, and how a plant grows

A wheat block carries its age in the block itself. A tripwire has nowhere to put one, so for a plant on the reserved host the ripeness lives in the chunk record beside the plot. Everything above that point, the harvest, the yield, the quality grade, the genotype and the pollination, reads one answer and never asks where it came from.

That seam exists for one reason: no number may reveal the host. Time to ripe, yield, quality and price are decided by the same code from the same reading whichever way the plant is stored. Only the picture is different.

Growth

A tripwire receives no crop random tick, so a plant on the reserved host would never grow on its own. A clock drives it instead, and it reproduces the real arithmetic rather than approximating it:

  • the farmland moisture in the three by three area under the plant, counted the way vanilla counts it, with the centre at full weight and the eight edges at a quarter;
  • the halving that vanilla applies when the same crop stands on both axes or on any diagonal, where same crop means the same original host block, so a tripwire wheat field halves exactly as a vanilla one;
  • the light gate where the host has one. Torchflower, wheat, beetroot and sweet berry want light level 9; pitcher, nether wart, cocoa and cave vines do not care;
  • the world's random tick speed and the server's growth modifier, both read live.

On top of that sits the garden's own multiplier, the one you already know from season, soil, water, greenhouse and blight. It is applied by the same call the vanilla path uses, so a blighted plot halts on the reserved host exactly as it halts on a vanilla one.

A brown mushroom hosted plant, meaning cave_mushroom, is not on a tick at all. It runs on a timer and the clock leaves it alone.

Bone meal

Bone meal works from either hand, from a dispenser, and from the NPC farmer. A dropper never applies it, which is vanilla behaviour. The click is resolved to the plant, so hitting the farmland under it or the upper half of a two-tall plant still feeds the right thing. The amount is the original host's own number, taken from the game's code:

HostWhat one bone meal does
WheatAdvances 2 to 5 ages
BeetrootsThat same draw divided by three, so it can be zero and still consume the meal, as in vanilla
Torchflower CropOne stage
Pitcher CropOne stage
Sweet Berry BushOne stage
CocoaOne stage
Cave VinesSets the berries
Nether WartRefused. Nether wart never took bone meal in vanilla
Brown MushroomRefused. It grows on its own clock

It always answers. A ripe plant is told to be harvested, a gated plant names the gate in the words the soil readout uses, a host that never took bone meal says so, and a timed plant says so. Lines are throttled to one every second and a half per player, so walking a field does not fill your chat.

What you see, and from when

Three kinds of viewer look at the same plant, and only one of them can see the art.

ViewerWhat is sent
Java, resource pack loadedNothing extra. The real block already carries the plant's texture
Java, no resource packThe vanilla crop, drawn for that player alone
BedrockThe vanilla crop, drawn for that player alone

The second and third cases are an illusion in the strict sense: a block change addressed to one player, changing nothing in the world. The record underneath is the same, the age is the same, and harvesting behaves the same. Without it a player with no pack would be standing in a field of bare string.

The join, which used to be the weak point

Chunks arrive before your resource pack status does, so at join every Java player looked like a player without the pack and was painted the vanilla crop over every plant. Then nothing ever un-painted it. The illusion survived until something wrote to the block for real, which is why plants used to snap to their proper texture only when you right-clicked one or when it grew.

Two things closed that in 2.0.5. First, the un-painting path now exists: when your pack status changes, the real blocks are sent to you and to nobody else. Second, and more useful, a server that requires the resource pack disconnects a Java client that declines it, so a Java player who is still connected has the pack whether the status packet has arrived or not. On such a server nothing is painted at join at all and the plant is correct from the first frame.

A Bedrock client is answered as having no pack before any of that is considered, in every combination, because sending a Bedrock player the real block would show them bare string.

Named rather than left silent. Grove centres carry the same join race and are not plots, so the repaint walk does not reach them. On a server that requires the pack the race does not arise for them either, which is why this is written down as a gap rather than a break.

What you can break, and what drops

A plant on the reserved host is a tripwire, and a tripwire drops string. That would be a quiet way to turn a garden into a string farm, so a plant never drops string on any path. Each route is handled at the point where it happens.

What you doWhat happens
Hoe or right-click a ripe plantHarvests and replants, from either half of a two-tall plant
Hoe an immature plantRefused, and told which stage it is at, in the same sentence the soil readout uses. One line per plant, not one per click
Break a ripe plant by handHarvests, then replants at stage zero
Break an immature plantThe whole plant is removed and the seed comes back, where seed return is configured
Break the upper half of a two-tall plantTakes the whole plant, same as breaking the lower
An upper half whose lower is goneNever harvests on its own. The click is redirected downward, but an orphan top is still refused, because harvesting it would be a free harvest
Break, trample or dry out the ground under a plantProduce if it was ripe, seed if not, and the plant is removed. A tripwire has no support rule of its own, so the plugin supplies one
Explosion or pistonThe string is swapped for the species seed and the record forgotten
Water or lava washing over itThe same swap
Harvest Scythe sweep or NPC farmerThe same funnel, each plant taken once, never twice from two heights

The two-tall case was the last one to close. A two-tall plant is genuinely two plots, and the upper one is marked as an upper half so nothing harvests it in its own right. That marking was also what made a hoe on the upper block do nothing at all, silently. The click is now redirected to the lower half at the single point every harvest route passes through, so the scythe's cube sweep, the NPC farmer's three-layer pass and an ordinary right-click all behave the same, and when the answer is still no, it says why.

Nothing re-skins a plant, and a real tripwire stays real

For a plant on the reserved host the state is the picture. The attached flag, the powered flag and the four direction bits together name one entry in the ledger, so a single bit flipping is not a decoration, it is a different plant. There is no connected look and unconnected look to choose between.

That makes the requirement blunt: no write the plugin did not make may change a property at a recorded plant position. Three things in vanilla try.

CauseWhat it would doWhat happens now
You step on itFlips the powered bitCancelled at a recorded plant position
A neighbouring block changesRewrites the four direction bitsThe cascade is cut, and a correction is queued for the next tick, because the write lands before the event can be cancelled
A tripwire hook lineRe-asserts itself on a schedule, through a path that raises no event at allThe cause is refused from both ends

The hook case is the one that needed new work. A line between two hooks reaches up to 42 blocks and rewrites every wire between them on a timer, forever, with nothing to listen for. So planting inside a live line is refused before the host block is written and before your seed is consumed, and placing a hook whose line would cross a plant is refused with the distance named. One hook is not a line, and refusing on one hook would refuse planting all over a decorated build, so a single hook is allowed. If an axis is not loaded the walk skips it and the plant is allowed, which fails toward letting you plant.

The correction is a repair rather than a rewrite: a queue, deduplicated so a storm of updates over one plant costs one fix, capped at 4096 entries, running on a task that already existed. Anything dropped by the cap converges the next time the chunk loads.

Your redstone is not affected

A genuine tripwire, meaning one with no plant record, connects, arms, triggers and drives redstone exactly as it always has. Every refusal and every cancellation above is scoped to a recorded position and never to the material. That distinction matters more than it looks: a material-scoped version would cancel physics and entity presses on every tripwire on the server, breaking redstone globally, and it would look exactly like this working correctly, since plants would indeed stop changing.

One known gap. A grove centre is not a crop plot and carries no positional record, so none of these arms protect it and a hook line through one will re-skin it. It converges back the next time anyone interacts with the grove, since that write is repeated on every interaction. Giving the grove centre a record of its own is the real fix and belongs to the grove system.

Placing a bloom as decoration

Until 2.0.5 no garden bloom could be placed at all. Right-clicking one at the ground answered that is a plant, it cannot be placed as a block, which was the item capability system doing its job: a bloom declares what it can be used for, and placing was never on the list.

The obvious fix was the wrong one. Simply allowing the placement would let vanilla place the base flower instead, destroying the custom identity, the species and the genotype, with no event and no message. So the capability is still denied, and the interaction is caught a step earlier: the click never becomes a block placement. What is written instead is the species' own plant, at its ripe look, on the reserved host. No new ledger entry is claimed, because the ledger is full at 64 of 64, and the ripe look for every species already exists.

The ground it accepts is the dirt family plus farmland, which is deliberately wider than the crop rule. Crops want farmland alone; a decorative bloom is happy on grass, dirt, podzol, moss, mud and farmland alike.

Decoration is not a money faucet

A ripe recorded plant is harvestable by every existing route, so place a bloom, harvest a ripe plant, receive produce and a seed would be a money pump assembled from two features that are each correct. One bit on the record closes it, and that bit is on the world record rather than on the item, so stacks in your inventory do not split.

A decor plantBehaviour
GrowthSkipped entirely. It stays at the look you placed
Harvest or breakExactly one bloom back. No seed, no produce
ReplantingNone. It is removed rather than reset, so it cannot be re-harvested on the spot
XP, job credit, sighting, quality, pollinationNone of them
/farm adoptCannot sweep it up

Every refusal costs you nothing. A ground block the tag does not accept, an occupied target, no headroom for a two-tall species, a protection or cultivation ban, a chunk whose record cap is full, or a write the world did not take: in each case the bloom stays in your hand and the message names the reason. Placement is a right-click, so it is the same gesture on Bedrock, and the refusals are plain text there.

Status. Everything on this page is written and build-checked, and is marked in the project's own notes as code-written and not server-verified. Where a number here comes from the game's own code it is transcribed from the jar rather than estimated, and where something is approximate or unproven the docs say so and so does this page.