Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chunk anatomy

Verified against Minecraft 26.2 · Part IV · One block is placed, and the write travels down through the chunk until it lands in four bits of one long.

You place a single block of deepslate at y −40, in a section that until now held nothing but stone and air. The click ends in LevelChunk.setBlockState, and the write goes down four objects: a LevelChunkSection sixteen blocks tall, a PalettedContainer of 4,096 block states, a palette that turns the state into a small integer, and a SimpleBitStorage that puts that integer into some fixed number of bits of one long. Everything else in this part moves chunks — tickets load them, the pyramid generates them, the light engine walks them, the region file stores them. This is the page that looks inside, and it is the vocabulary the rest of the part spends. Begin with the part that surprises people: a section holding two distinct block states costs exactly what one holding sixteen costs — four bits an entry, 256 longs, on disk as well as in memory — and the block that makes it seventeen re-encodes all 4,096 entries into a wider storage before it can be written.

The cast

classwhat it decidesthread
ChunkAccesseverything a chunk has whatever its shape: position, height, sections, heightmaps, block entities, structures, the two volatile flagsabstract — whichever thread owns the shape below
ProtoChunka chunk under construction: status, carving mask, entities as NBT, the light engine it reports towritten on the worker pool, one writer at a time
LevelChunka chunk that is part of a Level: block entities, tickers, tick containers, the full-status supplierthe server thread — on the client, the client’s main thread
ImposterProtoChunkwhat a still-generating neighbour sees when the chunk it asked for is already livethe server thread
LevelChunkSection16×16×16: two palette containers and four counters that let a whole section be skippedwhichever thread holds its permit
PalettedContainerthe mapping from 4,096 (or 64) entries to values, and when to widen itone writer at a time, enforced by ThreadingDetector; reads are lock-free
Strategywhich palette and which bit width each entry count deserves, for block states and for biomesimmutable, shared by every container in the level
Heightmapthe top of each of 256 columns, for one definition of topwith the chunk that owns it

The four shapes a chunk takes

flowchart LR
    NEW["nothing on disk: ChunkMap.createEmptyChunk"] --> PC
    DISK["the region file"] -->|"SerializableChunkData.parse, on the worker pool"| SCD["SerializableChunkData"]
    SCD -->|"read, on the server thread, stored status below full"| PC
    SCD -->|"read, stored status full: a LevelChunk is built, then wrapped"| IPC
    PC["ProtoChunk: generation state, written on the worker pool"] -->|"ChunkStatusTasks.full, on the server thread"| LC
    LC["LevelChunk: the live chunk, owned by the server thread"] -->|"GenerationChunkHolder.replaceProtoChunk"| IPC
    IPC["ImposterProtoChunk: a ProtoChunk-shaped view over a LevelChunk"] -->|"ImposterProtoChunk.getWrapped"| LC
    MISS["a lookup that finds nothing there"] --> ELC["EmptyLevelChunk: void air, and a LevelChunk itself"]

ChunkAccess is the abstract chunk and has exactly two direct concrete lines — ProtoChunk (ChunkType.PROTOCHUNK) and LevelChunk (ChunkType.LEVELCHUNK) — with ImposterProtoChunk a subclass of the first and EmptyLevelChunk of the second. Nothing else extends it.

Every one of them carries the same core. ChunkAccess.chunkPos says where it is, and a LevelHeightAccessor says how tall: LevelHeightAccessor.getMinY and LevelHeightAccessor.getHeight are the only two facts about height there are, and the overworld’s −64 and 384 give LevelHeightAccessor.getSectionsCount of 24, section Y −4 through 19. Beside them sit the heightmaps, the block entities in two maps (ChunkAccess.blockEntities live, ChunkAccess.pendingBlockEntities still NBT, ChunkAccess.getBlockEntitiesPos the union), ChunkAccess.structureStarts and ChunkAccess.structuresRefences (Mojang’s spelling), the per-section ChunkAccess.postProcessing offsets to revisit after load (ProtoChunk.packOffsetCoordinates packs four bits each of x, y and z into a short), ChunkAccess.inhabitedTime behind local difficulty (the level tick), ChunkAccess.upgradeData and the nullable ChunkAccess.blendingData whose presence is ChunkAccess.isOldNoiseGeneration (blending), and two volatile flags — ChunkAccess.unsaved, whose test-and-clear ChunkAccess.tryMarkSaved the saver uses, and ChunkAccess.isLightCorrect, saved as isLightOn. It is also three interfaces at once — LightChunk, which is what the light engine reads through LightChunk.findBlockLightSources and LightChunk.getSkyLightSources, plus StructureAccess and BiomeManager.NoiseBiomeSource — and ChunkAccess.getPersistedStatus is the ChunkStatus that goes to disk, with ChunkAccess.getHighestGeneratedStatus folding in BelowZeroRetrogen.targetStatus for a chunk still being deepened.

A ProtoChunk adds what only generation needs: a volatile ProtoChunk.status (ProtoChunk.setPersistedStatus also retires a finished BelowZeroRetrogen), a ProtoChunk.lightEngine from ProtoChunk.setLightEngine that it reports to only once the status ChunkStatus.isOrAfter ChunkStatus.INITIALIZE_LIGHT, its entities as a list of CompoundTag (ProtoChunk.addEntity serialises on the spot), a ProtoChunk.carvingMask, and ProtoChunkTicks that ProtoChunk.unpackBlockTicks turns into LevelChunkTicks on promotion (scheduled ticks). The pool that fills all of that in is the generation pipeline. Ask it for a biome before ChunkStatus.BIOMES and ProtoChunk.getNoiseBiome throws Asking for biomes before we have biomes.

A LevelChunk adds what only a live chunk needs: LevelChunk.level, LevelChunk.setLoaded, a supplier of FullChunkStatus the holder owns (tickets), two LevelChunkTicks that LevelChunk.registerTickContainerInLevel attaches to the level’s queues and LevelChunk.unregisterTickContainerFromLevel detaches, the ticker map, a one-shot LevelChunk.postLoad processor that LevelChunk.runPostLoad fires, the per-section LevelChunk.gameEventListenerRegistrySections — an EuclideanGameEventListenerRegistry each, built on demand and only on a server (game events) — and a LevelChunk.unsavedListener that LevelChunk.markUnsaved fires only on the false-to-true edge, which is how the server’s dirty set learns of a change without scanning. Its LevelChunk.getPersistedStatus is always ChunkStatus.FULL.

ImposterProtoChunk exists because a neighbour still generating asks the holder for “the chunk at status X” and must be handed something ProtoChunk-typed even when that chunk is already live. Reads delegate to ImposterProtoChunk.getWrapped; writes are dropped unless allowWrites, which both of the two places that construct one pass as false, so in 26.2 every write to an imposter is dropped — heightmaps, structure starts and references and block-entity NBT unconditionally, and the rest for want of the flag. ImposterProtoChunk.getSections hands back the wrapped chunk’s array unconditionally — only the single-section ImposterProtoChunk.getSection is gated — while ImposterProtoChunk.markUnsaved and ImposterProtoChunk.setLightCorrect always pass through and ImposterProtoChunk.canBeSerialized is false, because the LevelChunk under it is what gets saved. Its ImposterProtoChunk.fixType maps a request for a _WG heightmap onto the live one, but only inside ImposterProtoChunk.getHeight: asking it to create a _WG heightmap creates a real one on the live chunk.

EmptyLevelChunk is the other direction: Blocks.VOID_AIR everywhere, EmptyLevelChunk.isEmpty true where LevelChunk.isEmpty is false, one fixed biome, and EmptyLevelChunk.getFullStatus a flat FullChunkStatus.FULL. ClientChunkCache.emptyChunk is one shared instance handed out for any client miss, and only when the caller asked to load or generate — otherwise the miss returns null; PathNavigationRegion builds its own for whatever a mob’s pathfinder cannot see. The real client chunks live in ClientChunkCache.Storage, an AtomicReferenceArray ring whose ClientChunkCache.Storage.onSectionEmptinessChanged and its double-buffered added and removed sets are the renderer’s feed of which sections exist.

Sections and their four counters

flowchart TD
    LC["LevelChunk: one 16 by 16 column of the whole build height"] --> ARR["ChunkAccess.sections: an array of LevelChunkSection, 24 in the overworld, never a null slot"]
    LC --> HM["four Heightmaps: 256 entries of 9 bits each"]
    ARR --> ST["LevelChunkSection.states: PalettedContainer of BlockState, 4096 entries"]
    ARR --> BIO["LevelChunkSection.biomes: PalettedContainerRO of Biome, 64 entries, one per 4 by 4 by 4 quart"]
    ARR --> CNT["four shorts: nonEmptyBlockCount, fluidCount, tickingBlockCount, tickingFluidCount"]
    ST --> DATA["PalettedContainer.Data: one volatile record of configuration, palette and storage"]
    BIO --> DATA
    DATA --> PAL["the palette, on the block-state ladder: one value at 0 bits, then anything from 2 to 16 values at 4 bits, 17 to 256 hashed, then the registry itself. Biomes climb a shorter ladder"]
    DATA --> BST["the BitStorage: ZeroBitStorage, or a SimpleBitStorage of 256 longs at 4 bits, 342 at 5, 512 at 8"]

The array never has a hole: ChunkAccess.replaceMissingSections runs in the constructor and fills every empty slot with a fresh all-air section from the level’s PalettedContainerFactory, so nothing that walks sections checks for null.

The four counters are what make a section cheap to skip. LevelChunkSection.setBlockState adjusts all four from the outgoing and incoming state on every single write — LevelChunkSection.nonEmptyBlockCount (zero is LevelChunkSection.hasOnlyAir, which is also how LevelChunk.getBlockState answers air without touching a palette), LevelChunkSection.fluidCount, LevelChunkSection.tickingBlockCount and LevelChunkSection.tickingFluidCount — and LevelChunkSection.isRandomlyTicking is the or of the last two. That one boolean lets ServerLevel skip a section of solid stone without looking at any of its 4,096 blocks. Only a load from disk recounts: LevelChunkSection.recalcBlockCounts runs from the two-container constructor, whose only caller is SerializableChunkData.

Biomes share the section but are coarse and read-only. Two bits per axis, 64 entries of 4×4×4 blocks each — the number LevelChunkSection.BIOME_CONTAINER_BITS names, though the 2 that matters is the literal in Strategy.createForBiomes and no reader of the constant survives the decompile. The field is a PalettedContainerRO and the published one is never mutated: LevelChunkSection.fillBiomesFromNoise, LevelChunkSection.read and LevelChunkSection.readBiomes each build a replacement through PalettedContainerRO.recreate, fill it, and swap the reference. The block-state container is the only one anything writes to in place.

Two different copies leave a section. The saver takes LevelChunkSection.copy, a deep copy of both containers and all four counters, on the server thread inside SerializableChunkData.copyOf — the IO lane never sees a live section, and even the NBT encoding of the copy runs on the background pool (chunk storage). The client mesher takes something cheaper: a SectionCopy takes PalettedContainer.copy of the block-state container alone (nothing at all when the section is air) plus an immutable snapshot of the chunk’s block-entity map. A worker that means to write instead brackets its work with LevelChunkSection.acquire and LevelChunkSection.release.

The palette and the ladder it climbs

A PalettedContainer’s whole state is one volatile record, PalettedContainer.Data, holding a Configuration, a Palette and a BitStorage. PalettedContainer.get reads the record once into a local and takes no lock, because a resize swaps in a whole new record rather than editing the old one. PalettedContainer.read from the wire is the exception that proves it: PalettedContainer.createOrReuseData hands back the existing record whenever the incoming bit count wants the same configuration, and the palette and the long array are then overwritten in place — which is the ordinary case on a client, where ClientChunkCache reuses the live LevelChunk. Which record a given entry count deserves is decided by a top-level Strategy — no longer nested inside the container — whose Strategy.createForBlockStates and Strategy.createForBiomes are called once per level by PalettedContainerFactory.create over Block.BLOCK_STATE_REGISTRY and the biome registry (registries), which is why the global palette’s width is a runtime number and not a constant.

Strategy.getConfigurationForBitCount is the ladder, and block states and biomes climb different ones:

distinct values (bits needed)block statesbiomes
1 (0 bits)Strategy.ZERO_BITSSingleValuePalette + ZeroBitStoragethe same
2 … 8 (1–3 bits)Strategy.FOUR_BITS_LINEARLinearPalette, already 4 bitsStrategy.ONE_BIT_LINEAR / Strategy.TWO_BITS_LINEAR / Strategy.THREE_BITS_LINEARLinearPalette at its own width
9 … 16 (4 bits)Strategy.FOUR_BITS_LINEAR, still 4 bitsConfiguration.Global already
17 … 256 (5–8 bits)Strategy.FIVE_BITS_HASHMAPStrategy.EIGHT_BITS_HASHMAPHashMapPaletteConfiguration.Global — there is no hashed tier for biomes
moreConfiguration.GlobalGlobalPalette, the registry’s own IdMapConfiguration.Global

SingleValuePalette holds one value and asks for width 1 the moment a second arrives — which for block states means jumping straight to the 4-bit rung. LinearPalette is a flat array of 2^bits slots scanned by identity, HashMapPalette a CrudeIncrementalIntIdentityHashBiMap, and GlobalPalette writes nothing on the wire, maps an unknown value to id 0 and answers Palette.maybeHas with an unconditional yes. Each of them calls PaletteResize.onResize when it fills, and the container is its own PaletteResize: PalettedContainer.onResize builds the next record, PalettedContainer.Data.copyFrom walks every entry of the old storage through the old palette into the new one, the record is published, and only then is the value that triggered the growth added — under PaletteResize.noResizeExpected, which throws if a second growth were somehow needed.

SimpleBitStorage never lets an entry straddle a long: its SimpleBitStorage.valuesPerLong is 64 divided by the width, so 4,096 entries are 256 longs at four bits, 342 at five and 512 at eight, and the cell index comes from a multiply-shift table (SimpleBitStorage.MAGIC) rather than a division. ZeroBitStorage answers 0 for everything and shares one empty ZeroBitStorage.RAW array. An array of the wrong length raises SimpleBitStorage.InitializationException, which PalettedContainer.unpack turns into a DataResult error instead of a crash.

What packing actually buys

The two serialised forms differ. PalettedContainer.write is the wire: a bits byte, the palette, a fixed-size long array at exactly the in-memory width. PalettedContainer.pack is the disk (the palette and optional data fields of a PalettedContainerRO.PackedData, behind PalettedContainer.codecRW and PalettedContainer.codecROcodecs), and it re-encodes into a fresh HashMapPalette before asking Strategy.getConfigurationForPaletteSize for the width — the same ladder memory climbs. Packing therefore buys a smaller palette, not narrower entries: unreferenced entries are dropped, which can demote a container a whole rung, and a Configuration.Global container shrinks from Configuration.bitsInMemory to Configuration.bitsInStorage. PalettedContainer.unpack re-encodes on the way back only for Configuration.Global, whose Configuration.alwaysRepack is true — every Configuration.Simple rung reports one width for both, so its long array is adopted exactly as it lies on disk.

The six heightmaps

A Heightmap is 256 entries — one per column, indexed x + z·16 — in a SimpleBitStorage whose width is Mth.ceillog2 of height + 1, so 9 bits for a 384-tall world, stored relative to the minimum Y. Heightmap.getFirstAvailable is the first free Y and Heightmap.getHighestTaken the one below it. Heightmap.primeHeightmaps fills several types in a single top-down column scan (starting from the deprecated-for-removal ChunkAccess.getHighestSectionPosition) into maps that ChunkAccess.getOrCreateHeightmapUnprimed makes on demand. Heightmap.update is the incremental path, raising the height when an opaque block is placed at or above it and rescanning downward only when the block that turned transparent was the top one.

typeopaque meansusagesavedsent
Heightmap.Types.WORLD_SURFACE_WGnot airHeightmap.Usage.WORLDGENproto onlyno
Heightmap.Types.WORLD_SURFACEnot airHeightmap.Usage.CLIENTyesyes
Heightmap.Types.OCEAN_FLOOR_WGblocks motionHeightmap.Usage.WORLDGENproto onlyno
Heightmap.Types.OCEAN_FLOORblocks motionHeightmap.Usage.LIVE_WORLDyesno
Heightmap.Types.MOTION_BLOCKINGblocks motion or holds fluidHeightmap.Usage.CLIENTyesyes
Heightmap.Types.MOTION_BLOCKING_NO_LEAVESthe same, but not a LeavesBlockHeightmap.Usage.CLIENTyesyes

Which of the six a chunk carries follows its status: ChunkStatus.heightmapsAfter is the two _WG maps through ChunkStatus.SURFACE and ChunkStatus.FINAL_HEIGHTMAPS — the other four — from ChunkStatus.CARVERS on, and a LevelChunk is constructed with exactly those four. A ProtoChunk primes any of its status’s maps that are missing the first time a block is written. What is saved, though, is not Heightmap.Types.keepAfterWorldgen: the saver writes whatever the chunk’s persisted status names, so a proto chunk stored below ChunkStatus.CARVERS does save its two _WG maps. Separately and privately, ChunkAccess.skyLightSources is a second 256-entry bit storage — a ChunkSkyLightSources — that only the sky-light engine reads (lighting).

What placing a block actually does

LevelChunk.setBlockState is the one write path into a live chunk, and its order matters more than any single step in it:

in orderwhat happenswhen it is skipped
1the section is fetched and its emptiness rememberedair into an all-air section returns null immediately
2LevelChunkSection.setBlockState writes the palette entry and moves all four counters
3the old state is compared to the newidentical state returns null, and nothing below runs
4all four heightmaps take Heightmap.update
5if the section’s emptiness flipped: LevelLightEngine.updateSectionStatus and ChunkSource.onSectionEmptinessChangedwhen it did not flip
6if LightEngine.hasDifferentLightProperties: ChunkSkyLightSources.update, then LevelLightEngine.checkBlockwhen opacity and emission are unchanged and neither state uses a shape for light occlusion
7the old block entity is dropped, preceded on the server by BlockEntity.preRemoveSideEffectswhen the block did not change, had no block entity, or BlockBehaviour.BlockStateBase.shouldChangedStateKeepBlockEntity — the side effects alone are skipped on the client and under Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS
8BlockBehaviour.BlockStateBase.affectNeighborsAfterRemovalwhen the block did not change and the new one is no BaseRailBlock, off the server, or without Block.UPDATE_NEIGHBORS and not moved by a piston
9the section is re-read — if step 8 changed the block again, the call returns null
10BlockBehaviour.BlockStateBase.onPlaceon the client, or under Block.UPDATE_SKIP_ON_PLACE
11the new block entity is created or re-validated, and its ticker reboundwhen the new state has no block entity
12LevelChunk.markUnsaved

Two steps there are easy to misread. Step 8 is a genuine neighbour side effect inside the chunk write, but it is the removed block’s own clean-up — shape updates and redstone notifications belong to Level.setBlock, after the chunk returns (blocks and states). Step 9 exists because step 8 runs arbitrary block code that may write the same position again, and LevelChunk.setBlockState will not claim a placement it no longer owns.

ProtoChunk.setBlockState is the same idea with everything live stripped out: section write, light only past ChunkStatus.INITIALIZE_LIGHT, the status’s heightmaps updated (primed first if absent), and no block entity, no BlockBehaviour.BlockStateBase.onPlace, no neighbours at all.

The double indirection behind step 11

LevelChunk holds every block-entity ticker in the world’s hot loop, and it does so at one remove. LevelChunk.addAndRegisterBlockEntity sets the entity, registers its game-event listener and asks for its ticker; a LevelChunk.BoundTickingBlockEntity binds the entity to its BlockEntityTicker and gates every tick on LevelChunk.isTicking (inside the world border, at FullChunkStatus.BLOCK_TICKING or beyond, entities loaded). That sits inside a LevelChunk.RebindableTickingBlockEntityWrapper held in LevelChunk.tickersInLevel, so Level.blockEntityTickers keeps one stable handle per position for the life of the chunk and removal is only a LevelChunk.RebindableTickingBlockEntityWrapper.rebind to LevelChunk.NULL_TICKER, whose TickingBlockEntity.isRemoved is true and lets the level’s list prune itself. When the chunk first goes live, LevelChunk.postProcessGeneration replays the post-processing offsets, promotes every pending block entity and applies UpgradeData.upgrade.

Questions players ask

Why do two threads writing one section crash the game rather than block? Because PalettedContainer.threadingDetector is a detector, not a mutex. ThreadingDetector.checkAndLock tries a one-permit semaphore and, on failure, records itself as the loser and then blocks. It is the winner that notices, in ThreadingDetector.checkAndUnlock: it builds ThreadingDetector.makeThreadingExceptionAccessing PalettedContainer from multiple threads, with both stack traces — and throws it, and the loser re-throws the same report the instant it acquires the permit. Both threads die, deliberately: an interleaved section write would be a corrupt world rather than a crash. Exactly one thread writes a section at a time — the server thread for a live chunk, and on the worker pool whoever holds LevelChunkSection.acquire, either NoiseBasedChunkGenerator, which holds every section across its noise range, or the BulkSectionAccess that OreFeature — its only user — holds over every section it touches until it closes. Those hold the permit already, so they write through the unchecked five-argument LevelChunkSection.setBlockState and PalettedContainer.getAndSetUnchecked.

Why are a client chunk’s ticking counters zero? Because LevelChunkSection.write carries only two of the four shorts, the non-empty-block and fluid counts, and LevelChunkSection.read takes exactly those two and never recounts. A client section therefore starts with LevelChunkSection.tickingBlockCount and LevelChunkSection.tickingFluidCount at zero and only ever counts what has changed since the chunk arrived. Nothing notices: the only reader of LevelChunkSection.isRandomlyTicking is ServerLevel, and the client runs no random ticks.

Why does the proto chunk keep working after the level chunk exists? Because the two share the sections but not the array. ChunkAccess always allocates its own array and copies the references in, so promotion leaves two chunks holding two arrays over one set of LevelChunkSection objects — writing through either is writing the same blocks. That is also what makes the ImposterProtoChunk honest: it is a third handle on the same sections.

Why does a chest in a freshly loaded chunk not exist yet? Because it is still a CompoundTag in ChunkAccess.pendingBlockEntities (block entities). Any call to LevelChunk.getBlockEntity promotes it through LevelChunk.promotePendingBlockEntity on the first touch, whatever the LevelChunk.EntityCreationType asked for; LevelChunk.postProcessGeneration and LevelChunk.registerAllBlockEntitiesAfterLevelLoad promote the rest in bulk when the chunk goes live. Of the three creation types only LevelChunk.EntityCreationType.IMMEDIATE and LevelChunk.EntityCreationType.CHECK have callers left in the game — LevelChunk.EntityCreationType.QUEUED has none.

Why can a search skip a whole section without reading it? Because LevelChunkSection.maybeHas puts the predicate to the palette alone, so ChunkAccess.findBlocks can rule out 4,096 blocks with a handful of comparisons — unless the section is on the global palette, whose answer is always maybe.

What does the client actually receive? ClientboundLevelChunkWithLightPacket, whose ClientboundLevelChunkPacketData carries the heightmaps for which Heightmap.Types.sendToClient is true (three of the six), one buffer holding every section’s LevelChunkSection.write — empty ones included — and the block-entity update tags. The writer pre-sizes that buffer from the sum of LevelChunkSection.getSerializedSize and throws if ClientboundLevelChunkPacketData.extractChunkData does not fill it to the byte, and the reader refuses anything over two megabytes. Light rides beside it in ClientboundLightUpdatePacketData. The client applies the lot through ClientPacketListener.updateLevelChunkClientChunkCache.replaceWithPacketDataLevelChunk.replaceWithPacketData, which clears the block entities, gives each section LevelChunkSection.read, installs the raw heightmaps with ChunkAccess.setHeightmap and rebuilds the sky-light sources. Biome-only refreshes come later as ClientboundChunksBiomesPacketLevelChunk.replaceBiomes, and the block-entity tags travel as ClientboundLevelChunkPacketData.BlockEntityInfo.

Where to look

ChunkAccess · LevelChunk.setBlockState · LevelChunk.getBlockEntity · ProtoChunk.setBlockState · ImposterProtoChunk · ChunkStatusTasks.full · LevelChunkSection.setBlockState · LevelChunkSection.write · PalettedContainer.onResize · PalettedContainer.pack · PalettedContainer.unpack · Strategy.createForBlockStates · Configuration.Global · SimpleBitStorage · ThreadingDetector.checkAndLock · BulkSectionAccess · Heightmap.Types · ChunkStatus.heightmapsAfter · ClientChunkCache.Storage · ClientboundLevelChunkPacketData.extractChunkData · the class index for every field no page names


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.