Gameplay / world

Hay portals & the horse dimension

Build a hay-bale frame, light it with a golden carrot, and stand in it for ten seconds. You arrive in a private, disposable corridor of 2,000 pens, each holding a mare and a stallion of one randomly rolled genotype, each signed with the genome they carry. It is the mod’s showroom — a place to see what the genes actually do without breeding for a week.

The names still say “debug”

The dimension is a normal feature now, reachable by portal in any build. Only the F6 shortcut is dev-gated. The class and registry names — DebugPenManager, DEBUG_LEVEL, horsegenetics:debug_pens — are legacy from when it was debug-only; a rename is deferred because it would break existing save data.

Hay-bale portals

Lighting one

Build a vertical frame of hay bales around an empty rectangle, at nether-portal inner sizes (2–21 wide, 3–21 tall), outside the horse dimension. Right-click any frame block with a golden carrot.

server/HorsePortalManager.findFrame tries every in-plane air neighbour of the clicked bale as a flood-fill seed — taking only the first is what used to make a corner click silently do nothing — floods the enclosed air in the frame plane (X first, then Z), requires a filled rectangle fully bounded by hay, and fills it with horsegenetics:hay_portal. The carrot is consumed, and you get a chat line either way; on failure it states the size rule.

The portal block

hay_portal is a cosmetic, indestructible BaseEntityBlock — no collision, light level 11, no loot, RenderShape.INVISIBLE. Its look is a half-block-thick opaque slab drawn by HayPortalRenderer, inset along the axis the portal plane doesn’t run along, so it faces the player rather than being edge-on.

Going through

server/PortalEventHandler counts ticks an entity spends standing in a portal block (feet and y+0.9):

WhoDwellWhat happens
Player, overworld portal200 ticks (10 s)A fresh plot is allocated and you are dropped at its entrance, facing the corridor, with a paper in the first free hotbar slot. The return is wired back to this portal.
Player, dimension portal200 ticksBack to where you came from — and your tamed horses come with you (below).
Horse60 ticks (3 s)Goes through on its own.

While the counter runs, the entity is ringed with gold dust particles (denser as it nears zero — vanilla has no gold portal particle), and a player gets a one-time “grabs hold” line plus a per-second countdown for the last five seconds. A 100-tick cooldown blocks instant re-triggering.

The roped-horse shortcut

Right-click a hay_portal while leading horses and every leashed horse within 12 blocks is snapped to the portal block, has its lead dropped, and has its dwell counter seeded so it goes through in about three seconds. Owner-verified.

Coming back

As a player’s exit dwell starts they are warned in chat that anything left behind is lost and that tamed horses come with them. At teleport, evacuateTamedHorses takes your tamed horses — or every tamed horse, if you are the last player out. Each is dropped about two blocks above the return portal on a 3-wide grid with 10 seconds of invulnerability, so the short fall cannot hurt it, and is parked in the portal cooldown so it cannot be sucked straight back through. No terrain is carved.

One private plot per visit

The dimension generates nothingthe_void biome, a single air layer. Every visit gets its own DebugPenManager.Plot: a corridor built at a unique world X, with plots 8,007 blocks apart (the corridor length plus a thousand) and X slots recycled through a free list, all at a fixed PLOT_BASE_Y of 128. Plots never share chunks, so “two players never land in the same place” holds with no per-player-dimension machinery.

PLOTS is strictly one plot per player: entering makes a new one and tears down your previous one, so a revisit always regenerates and nothing you left behind survives.

Leaving clears entities, not blocks

The geometry is fixed, so an X slot handed back to the free list is rebuilt byte-identically on top of the old one. Teardown is therefore O(entities), not O(blocks walked): it discards non-player entities in the plot’s AABB and forgets each one from the ancestry database, or every visit would leave hundreds of throwaway pen records in the save forever. The AABB covers the whole X slot, not just what this visit built, or a slot where a previous visitor walked further would accumulate horses indefinitely.

A record that references a forgotten horse as a parent is left alone — ancestorsOf already skips ancestors it cannot find, so a foal bred in the dimension and taken home keeps working with a missing parent node.

What is in the pens

Each pen is one random genotype, shown by two horses. The corridor is a fixed PEN_COUNT of 2,000 — an arbitrary number, deliberately not derived from the genotype space, so adding a gene does not lengthen the corridor. Both horses in a pen share the genotype but not the epigenome, so they are two examples rather than two copies.

A way out every twenty-five pens

Every 25th segment carries no pens. It is a break with a hay-bale portal home. The corridor is some seven thousand blocks end to end and until now had exactly one way out, the return portal at the entrance — so anybody who walked in a few hundred pens to look at something had to walk all the way back. There are 41 of these, one every 175 blocks, so a way home is never more than about ninety blocks away.

ShowcaseGenotypes — a founder draw with a floor under it

An honest wild draw is mostly plain horses: every founder table is weighted hard toward its baseline, which is correct for a wild spawn and useless for a corridor whose job is to show what the genes do. So common/genetics/ShowcaseGenotypes.random rolls an ordinary Genotype.random and then guarantees a floor:

Two rules keep it honest. Forcing only ever keeps a combination that actually expresses in this genotype (Genotype.shows), so a chestnut is never handed an agouti it will not paint. And a combination that masks — one that hides everything else — neither counts toward the floor nor is ever forced, or a masking gene would quietly exempt part of the corridor from the guarantee. Candidates are derived from the registry, so a drop-in gene joins the pool on its own.

The floor guarantees expressed, not perceptible

Mushroom on a black horse is a real expression that looks like nothing. Nothing in the model can currently answer “would a player see this”, so this is accepted rather than special-cased.

Signs

Layout

Cross-section from the road outward, mirrored on both sides:

road (gravel, z -3..3) | pen 6 x 20, brick walls | gravel strip + glowstone above (z 24) | oak-plank wall (z 25) | bedrock core (z 26) | VOID

The constants, by name

Every one of these is a field on server/DebugPenManager. They are worth naming because the geometry is fixed: an X slot handed back to the free list is rebuilt byte-identically on top of the old one, which is what lets teardown be O(entities) instead of O(blocks walked).

ConstantValueWhat it is
PEN_COUNT2 000Pens per corridor — an arbitrary number, deliberately not derived from the genotype space. Adding a gene must not lengthen the corridor.
PERIOD7PEN_LEN_X (6) + 1. LAST_SEGMENT_INDEX is 999, so the corridor is about 7 000 blocks.
PLOT_SPACING_X8 007The corridor + 1 000. Plots never share chunks, so “two players never land in the same place” holds with no per-player-dimension work.
PLOT_BASE_Y128The grass surface. Fixed, which is half of why a recycled slot rebuilds identically.
PEN_DEPTH_Z / PEN_FAR_Z20 / ±23Pen depth, and the back edge the E/W walls sit flush against — no grass gap.
GRAVEL_STRIP_Z±24Gravel, with a glowstone line floating directly above at baseY + 10.
WALL_PLANK_Z±25A single oak-plank wall, baseY to baseY + 9.
WALL_BEDROCK_Z±26The bedrock core, baseY - 3 to baseY + 9. Past it: open void.
LOOKAHEAD_PENS30Built up front; the rest are laid as the player walks.
HERD_RADIUS equivalent — n/a hereThe corridor has no herds; wild herd formation is a breeds concern.

The wall behind the return portal (buildStartCap) is the same sandwich turned ninety degrees, pushed two blocks back to originX - 3: bedrock core at originX - 3, a single oak-plank wall at originX - 2 on a bedrock/dirt base, and a gravel floor strip at originX - 1 with the glowstone line floating above it. The E/W walls are carried out to originX - 1 so the corner has no gap. An earlier version made originX - 1 a full-height gravel column with no base; it fell into the void and left a hole, which is why everything now stands on bedrock and dirt. The return portal itself (hay frame + hay_portal, axis Z) is at originX + 1, and the entrance sign three blocks in front of it at originX + 4.

House rules inside the dimension

The test yard

Thirty blocks to the right of where you arrive — you land facing +X, so right is +Z — a five-wide gravel path leaves the road through a door cut in the corridor wall and opens into a walled yard. It takes the place of the first pen on that side, which DebugPenManager.buildSegment deliberately does not build; the pen numbers are unchanged, so the yard costs one pen rather than shifting the rest.

It exists for the genes that cannot be judged in a pen and should not be judged in a real save — each a claim about something that happens while nobody is watching, or about a cost rather than a picture.

Since 2026-09-12 it is built for a night rather than a visit

The owner's brief: “I'm going to leave the game and the horse dimension running over night. Add as many time-reliant tests as possible, focusing on those that don't require direct intervention from me.” That rules out more than it lets in. A pen belongs in here now only if its test starts itself, runs on a clock and leaves something readable in the morning — and the third of those is the watch, which is new, because until then nothing in the repo took a reading of anything.

PenForWhat it is, and what a pass looks like
Egg layerthe produce accumulation capFour layers in one 16 × 17 pen — four, because one horse can essentially never reach its own cap. The guard is eight of the item within six blocks; a copy's interval is 4 000–14 000 ticks and a dropped item despawns at 6 000, so a single layer's floor clears itself about as fast as it fills. Pass: the count climbs, stops at or below eight, and the log shows drops refused rather than items quietly expiring.
Spawner roomholy wardA roofed, unlit stone box with a real zombie spawner, charged with zombies at build time rather than left empty — an empty spawner produces nothing, which is indistinguishable from the ward having stopped it. The warded horse stands outside the door, because a horse shut in with a spawner is a horse being hit by zombies. Pass: hundreds of monster spawned lines with a floor under their distance column.
Sound herdthe sound genes' cooldownsThree meowing and two singers in one pen. “Are these bearable in a herd” sounds like a question only ears can settle and is not: a herd firing eighty sounds in two minutes is unbearable by arithmetic. Every play is counted into the census. Meowing and singer rather than base alarm, which is gated on hostile_near and would sit silent here — a blank that reads exactly like a broken gene.
Intimidatingthe repel auraOne horse with six cows ringed round it at three blocks. Measured as a distance, not a count: penned cows cannot leave, so four in the far corner and four round its feet are the same number and opposite results. Pass: the nearest-creature figure climbs within a minute and sits at eight or more.
Growing rowdryad ×3, hot bloodedFour pens, each floored with what its own gene acts on. Three dryads, because the locus stopped being one thing on 2026-09-13 and each pen asks a different question: Oak/Brch for the new half-rate mixed rule (oak and birch are different blocks, so the watch reads the ratio rather than taking it on trust); Dark/Dark for whether the dark oaks cluster into a 2 × 2; and Mush/Mush on a floor that is half podzol and half grass - a mushroom survives any light on podzol and needs darkness on grass, so in a lit yard the pass is mushrooms on one half and none on the other. A pen that was all one thing could not tell “canSurvive is being consulted” from “the gene does not work”, which is the distinction the whole change is about. The floor is the test: convert() is a whitelist per cover, so a spreading gene given the wrong ground fails in a way indistinguishable from a broken one. The dryad's is grass, for saplings to stand on, and its watch box is six blocks tall because the far end of that test is a grown tree. The hot-blooded pen is snow with a strip of ice in it — ice being the half that melts to a water source rather than to air, and therefore the half that could flood something.
Bone mealdryad Bone/BoneSaplings and grass for it to hurry along, and a strip of wheat on farmland that it must never touch. The crop carve-out cannot be observed - a crop nobody fertilised comes up anyway on random ticks, so a grown wheat proves nothing either way. What settles it is the list of what the gene did touch: every fertilising writes a line, and a night of them with no crop among them is the pass.
Display roweight unverified coat genesEight stalls, two horses each, on a pale base — chestnut with one cream copy, because a stocked horse names only the locus under test and every other locus falls to black, where a dark marking simply is not there. Two horses and not one, because most of these are questions about variation (do the drips have their own lengths, does the emblem land somewhere new) and one horse cannot answer one. The row splits around the yard's centre line so it does not wall the gate off from everything behind it. This replaces two whole hotbar batches: the only way to run one of these used to be find the egg, spawn, look, repeat.
Molten rowmolten hooves ×4The four alleles side by side — white as a heterozygote, since one copy is supposed to be enough and nothing has checked that it is. The rendering is owner-confirmed; whether the four differ is not, and “does this one differ from the last” is a question nobody can settle from memory two spawns apart.
Lava channelfireproof, riddenA 19-block channel of lava, two wide and two deep, with dry stone at both ends and a saddled fireproof pair beside it. The float and the rider's immunity are confirmed; what is left is a number, and gap 179 asks whether a crossing at vanilla's fixed 0.02 is acceptable or worth this project's first mixin. That cannot be answered by one block of lava — it needs a run long enough to be boring. Two things this pen deliberately cannot show: the horse survives lava whether or not it is fireproof, because the dimension cancels all horse damage, and the rider survives whether or not the gene protects them, because the test world is creative. Both are already confirmed elsewhere; what is being measured here is speed.
Hydrophobichydrophobic, riddenHalf the pen is a pool two blocks deep — the gene ejects above 0.6, so this is well past the threshold. Half and not all, because you have to be riding before the test can happen and you cannot comfortably mount a swimming horse: a pen that was all water would be a test whose first step is impossible. The gate is on the dry side and the pair come saddled. Expect it to be half-built rather than broken — the “then heads for the nearest shore” half was never written, so it will happily dump you in the middle, and that is the finding.
Starburst, F8 herd, glow room§0-CP, §0-CQ, §0-COThe three that still want a person. They stay because they are cheap to do on the way past, not because they suit a night.

Every horse in the yard is tamed. Vanilla refuses to breed an untamed horse, the test kit's legend had been promising tamed ones since the day the yard was stocked, and the stocked row was not — so “breed a starburst pair in the yard” was an instruction nothing in the world could carry out. They are tamed with no owner, which is load-bearing: see the leak note below.

A confirmed gene loses its pen. Verdant's three floors, the pack leader's cows and the spontaneous-breeding field have all been here and are gone — the last two by the owner's call on 2026-09-12 (“we're done with those tests”). A pen for a settled question is a pen somebody has to walk past to reach a live one.

It is flat and plain on purpose. Every one of these tests is a count or a “did that still work”, and both are ruined by terrain: a flat floor inside a bedrock box means a missing horse did not fall down a hole and a stalled farm did not stall because something spawned in a cave.

The watch — DebugWorldWatch

ActionTrace logs things a person does. Every test above is the opposite: a sapling that appeared, snow that stopped melting, eggs that piled up and then stopped. None of those is an event anybody fires — they are differences between two readings. So each pen registers itself as a named box with a list of blocks worth counting, and two clocks run over them.

The yard's chunks are force-loaded, and that is what makes a night mean anything

Minecraft only ticks what is near a player. Blocks stop getting random ticks, entities stop moving, and a gene on a 9 000-tick timer simply does not fire. The yard is 110 blocks deep and its far end is out of range of its near end, so any place to stand leaves part of it asleep — which in the morning reads exactly like the gene at that end being broken.

So entering forces the yard's chunks (about twenty-five) and leaving releases them, and any left behind by a crash are swept at server start: a dimension that quietly ticked for ever afterwards would be a bug this tool caused rather than found. What the player must still do is stay logged in and off the pause menu — singleplayer stops the server on Esc — and not leave the dimension, which tears the plot down.

It had no night until 2026-09-12, and glowstone means night is not enough

26.1.2 made time per dimension, and it takes TWO fields, not one. A dimension_type names a default_clock - and naming one is not enough. Sky darkness is not read off the clock at all: Level.isBrightOutside() is skyDarken < 4, and skyDarken comes from the SKY_LIGHT_LEVEL environment attribute. What makes that attribute vary with the clock is the separate timelines field, which defaults to empty.

So debug_pens had the overworld's clock and no timelines: the clock advanced, nothing listened, and the sky stayed bright for ever. It carries "timelines": "#minecraft:in_overworld" now, the same tag vanilla's overworld uses.

This was found by a diagnostic rather than by reading, and that is the point of it. The owner reported "night isn't working, it still looks like day", which has three causes a chat box cannot separate. /testkit night was made to answer back, and it said "Night set. This dimension now reads DAY" - one line, and the glowstone explanation was eliminated in a second.

And the clock is only half of it. The corridor and the yard are lit by glowstone lines by design, and block light does not care what time it is - a glowing horse under one is still a horse you cannot see glowing. The yard's glow room is a roofed, unlit stone box holding a tron Trs/Trg pair and two lanterns, and it is the only genuinely dark place in the dimension. Three more went in on 2026-09-13, because §0-CO had named the genes that should actually show the new glow falloff and none of them was in here: gamma, whose SPOTS at softness 1.8 is the softest lit mask in the mod and the one that section says to look at; rime at 0.5 as the distant fourth; and one horse carrying two glowing genes at once, because overlapping lit texels are supposed to take the brighter of the two rather than summing — blown out to flat white where they cross means that rule has broken.

Three things that would have leaked, and one that says so

The yard is the first thing in the dimension built outside the corridor walls, and a plot's bounding box is what tears the plot down and what walks tamed horses home. plotBox covers the yard's extent (DebugTestYard.FAR_Z, WEST_DX, EAST_DX), or anything built out there would outlive its plot and pile up on a recycled origin for ever.

And it walks home tamed and owned horses, not merely tamed ones. The moment the yard's own stock became tame, “tamed” stopped meaning “yours”: leaving the dimension would have deposited two dozen pieces of scenery at the overworld portal and left an empty yard behind. A horse a player tamed has an owner (tameWithName sets one); the yard's are set tame with none, and that is the line between somebody's horse and the furniture.

Because nobody can watch this code run, it reads the yard back after building it: the path's centre line and a grid over the floor, checking each spot has something to stand on and room to stand in. One line in the log either way — test yard built and walkable, or a count of the holes. A floor with a gap in it looks like nothing at all until somebody falls through it.

The genotype catalogue, and why the corridor stopped using it

The corridor was once one pen per visually distinct genotype, driven by GenotypeCatalog. The white-pattern rewrite took that to 2,064,387 pens — a corridor of about 7.2 million blocks, a quarter of the way to the world border — and it has grown by three orders of magnitude since. The gallery was retired before anyone ever walked it.

GenotypeCatalog survives, but as arithmetic rather than a list: size() is computed, and get(i) reads an odometer over each gene’s distinct pairs. Nothing in production indexes it — it is there for the tests and for a future punnett display. Its entries() is a lazy view, so streaming it still walks the whole space; sample it, or ask the arithmetic.

Known limitations

Code: server/DebugPenManager, server/HorsePortalManager, server/PortalEventHandler, block/HayPortalBlock, common/genetics/ShowcaseGenotypes.